wanshenmean
2026-02-11 75f34e9ba2e8b249c96333f3d7936c8968e12ec7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        /// <summary>
        /// ¼ìË÷µ±Ç°ÔÚ·þÎñÖÐ×¢²áµÄËùÓпͻ§¶Ë±êʶ·ûµÄÖ»¶ÁÁÐ±í¡£
        /// </summary>
        /// <remarks>·µ»ØµÄÁбí±íʾµ÷ÓÃʱ¿Ì¿Í»§¶ËIDµÄ¿ìÕÕ¡£ºóÐø¶Ô¿Í»§¶Ë¼¯ºÏµÄ¸ü¸Ä²»»áÓ°Ïì·µ»ØµÄÁÐ±í¡£´Ë·½·¨ÊÇḬ̈߳²È«µÄ¡£</remarks>
        /// <returns>°üº¬¿Í»§¶ËIDµÄ<see cref="IReadOnlyList{String}"/>¡£Èç¹ûûÓпͻ§¶Ë×¢²á£¬ÁÐ±í½«Îª¿Õ¡£</returns>
        public IReadOnlyList<string> GetClientIds()
        {
            lock (_syncRoot)
            {
                return _clients.Keys.ToList();
            }
        }
 
        /// <summary>
        /// ¼ìË÷ÓëÖ¸¶¨É豸±êʶ·û¹ØÁªµÄ¿Í»§¶Ë±êʶ·û¡£
        /// </summary>
        /// <remarks>´Ë·½·¨ÊÇḬ̈߳²È«µÄ¡£Èç¹ûδÕÒµ½É豸±êʶ·û£¬·½·¨½«·µ»Ønull¶ø²»ÊÇÅ׳öÒì³£¡£</remarks>
        /// <param name="deviceId">Òª¼ìË÷¿Í»§¶Ë±êʶ·ûµÄÉ豸µÄΨһ±êʶ·û¡£²»ÄÜΪnull¡£</param>
        /// <returns>ÓëÖ¸¶¨É豸±êʶ·û¹ØÁªµÄ¿Í»§¶Ë±êʶ·û£¬Èç¹û²»´æÔÚ¹ØÁªÔò·µ»Ønull¡£</returns>
        public string? GetClientIdByDevice(string deviceId)
        {
            lock (_syncRoot)
            {
                return _deviceBindings.TryGetValue(deviceId, out var cid) ? cid : null;
            }
        }
 
        /// <summary>
        /// Òì²½ÏòÖ¸¶¨É豸·¢ËÍÏûÏ¢¡£
        /// </summary>
        /// <remarks>Èç¹ûÖ¸¶¨É豸δע²á»òÎÞ·¨ÕÒµ½£¬Ôò·µ»Ø <see langword="false"/>¡£</remarks>
        /// <param name="deviceId">Ä¿±êÉ豸µÄΨһ±êʶ·û¡£²»ÄÜΪnull»ò¿Õ¡£</param>
        /// <param name="message">Òª·¢Ë͸øÉ豸µÄÏûÏ¢¡£²»ÄÜΪnull¡£</param>
        /// <returns>±íʾÒì²½²Ù×÷µÄÈÎÎñ¡£Èç¹ûÏûÏ¢³É¹¦·¢ËÍ£¬ÈÎÎñ½á¹ûΪ <see langword="true"/>£»
        /// ·ñÔòΪ <see langword="false"/>¡£</returns>
        public Task<bool> SendToDeviceAsync(string deviceId, string message)
        {
            var clientId = GetClientIdByDevice(deviceId);
            if (clientId == null) return Task.FromResult(false);
            return SendToClientAsync(clientId, message);
        }
 
        /// <summary>
        /// Í¨¹ýTCPÁ¬½ÓÒì²½ÏòÖ¸¶¨¿Í»§¶Ë·¢ËÍ´øÖ¡µÄÎı¾ÏûÏ¢¡£
        /// </summary>
        /// <remarks>Èç¹û¿Í»§¶ËδÁ¬½Ó»ò²»´æÔÚ£¬´Ë·½·¨½«·µ»Ø <see langword="false"/> ÇÒ²»·¢ËÍÏûÏ¢¡£
        /// ÏûÏ¢½«ÓÅÏÈʹÓÿͻ§¶ËÊ×Ñ¡µÄÎı¾±àÂë½øÐбàÂ룻·ñÔòʹÓÃĬÈϱàÂë¡£
        /// ´Ë·½·¨¶ÔÓÚÏò²»Í¬¿Í»§¶ËµÄ²¢·¢µ÷ÓÃÊÇḬ̈߳²È«µÄ¡£</remarks>
        /// <param name="clientId">Òª·¢ËÍÏûÏ¢µ½µÄ¿Í»§¶ËµÄΨһ±êʶ·û¡£±ØÐë¶ÔÓ¦ÒÑÁ¬½ÓµÄ¿Í»§¶Ë¡£</param>
        /// <param name="message">Òª·¢Ë͸ø¿Í»§¶ËµÄÎı¾ÏûÏ¢¡£²»ÄÜΪnull¡£</param>
        /// <returns>±íʾÒì²½²Ù×÷µÄÈÎÎñ¡£Èç¹ûÏûÏ¢³É¹¦·¢ËÍ£¬ÈÎÎñ½á¹ûΪ <see langword="true"/>£»
        /// ·ñÔò£¬Èç¹û¿Í»§¶ËδÁ¬½Ó»ò²»´æÔÚ£¬½á¹ûΪ <see langword="false"/>¡£</returns>
        public async Task<bool> SendToClientAsync(string clientId, string message)
        {
            TcpClient? client;
            SemaphoreSlim? sem = null;
            Encoding? enc = null;
            lock (_syncRoot)
            {
                _clients.TryGetValue(clientId, out client);
                _clientLocks.TryGetValue(clientId, out sem);
                _clientEncodings.TryGetValue(clientId, out enc);
            }
 
            if (client == null || !client.Connected)
            {
                return false;
            }
 
            enc ??= _textEncoding;
 
            if (sem != null) await sem.WaitAsync();
            try
            {
                var ns = client.GetStream();
                var framedMessage = BuildFramedMessage(message);
                var data = enc.GetBytes(framedMessage);
                await ns.WriteAsync(data, 0, data.Length);
            }
            finally
            {
                if (sem != null) sem.Release();
            }
            return true;
        }
 
        /// <summary>
        /// Òì²½ÏòËùÓÐÒÑÁ¬½ÓµÄ¿Í»§¶Ë·¢ËÍÖ¸¶¨µÄÏûÏ¢¡£
        /// </summary>
        /// <remarks>Èç¹ûÏòij¸ö¿Í»§¶Ë·¢ËÍÏûϢʱ·¢Éú´íÎó£¬Òì³£½«±»ÒÖÖÆ²¢¼ÌÐøÏòÆäËû¿Í»§¶Ë¹ã²¥¡£
        /// µ±ËùÓз¢ËͲÙ×÷Íê³Éºó£¬´Ë·½·¨½áÊø¡£</remarks>
        /// <param name="message">Òª¹ã²¥¸øËùÓпͻ§¶ËµÄÏûÏ¢¡£²»ÄÜΪnull¡£</param>
        /// <returns>±íʾÒì²½¹ã²¥²Ù×÷µÄÈÎÎñ¡£</returns>
        public async Task BroadcastAsync(string message)
        {
            List<TcpClient> clients;
            lock (_syncRoot)
            {
                clients = _clients.Values.ToList();
            }
 
            await Task.WhenAll(clients.Select(c => Task.Run(async () =>
            {
                try { await SendMessageAsync(c, message); } catch { }
            })));
        }
 
        /// <summary>
        /// Í¨¹ýÍøÂçÁ÷Òì²½ÏòÖ¸¶¨µÄTCP¿Í»§¶Ë·¢ËÍ´øÖ¡µÄÎı¾ÏûÏ¢¡£
        /// </summary>
        /// <remarks>Èç¹û¿Í»§¶ËΪnull»òδÁ¬½Ó£¬´Ë·½·¨½«Á¢¼´·µ»Ø¶ø²»·¢ËÍÏûÏ¢¡£
        /// ÏûÏ¢½«Ê¹ÓÃÅäÖõÄÎı¾±àÂë½øÐбàÂë²¢Ìí¼ÓÖ¡Í·ºóͨ¹ýÍøÂçÁ÷·¢ËÍ¡£</remarks>
        /// <param name="client">Òª·¢ËÍÏûÏ¢µ½µÄTCP¿Í»§¶Ë¡£±ØÐë´¦ÓÚÁ¬½Ó״̬£»·ñÔò·½·¨²»Ö´ÐÐÈκβÙ×÷¡£</param>
        /// <param name="message">Òª·¢Ë͸ø¿Í»§¶ËµÄÎı¾ÏûÏ¢¡£ÏûÏ¢ÔÚ´«Êäǰ½«±»±àÂë²¢Ìí¼ÓÖ¡Í·¡£</param>
        /// <returns>±íʾÒì²½·¢ËͲÙ×÷µÄÈÎÎñ¡£</returns>
        private async Task SendMessageAsync(TcpClient client, string message)
        {
            if (client == null || !client.Connected)
            {
                return;
            }
 
            NetworkStream stream = client.GetStream();
            var framedMessage = BuildFramedMessage(message);
            var data = _textEncoding.GetBytes(framedMessage);
            await stream.WriteAsync(data, 0, data.Length);
        }
    }
}