wanshenmean
10 天以前 035f2a81a59532ac9f892dab9ade44304847b4fb
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
141
142
143
144
145
146
147
148
149
150
151
152
153
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>
        /// 获取所有已连接客户端 ID 列表
        /// </summary>
        /// <remarks>
        /// 返回当前在服务器注册的客户端标识列表。
        /// 这是一个只读列表的快照,线程安全。
        /// </remarks>
        /// <returns>客户端 ID 列表</returns>
        public IReadOnlyList<string> GetClientIds()
        {
            lock (_syncRoot)
            {
                return _clients.Keys.ToList();
            }
        }
 
        /// <summary>
        /// 根据设备 ID 获取客户端 ID
        /// </summary>
        /// <remarks>
        /// 在设备绑定表中查找对应的客户端 ID。
        /// </remarks>
        /// <param name="deviceId">设备唯一标识</param>
        /// <returns>客户端 ID,如果未找到则返回 null</returns>
        public string? GetClientIdByDevice(string deviceId)
        {
            lock (_syncRoot)
            {
                return _deviceBindings.TryGetValue(deviceId, out var cid) ? cid : null;
            }
        }
 
        /// <summary>
        /// 异步向指定设备发送消息
        /// </summary>
        /// <remarks>
        /// 通过设备 ID 查找对应的客户端连接,然后发送消息。
        /// 如果设备未注册或连接不存在,返回 false。
        /// </remarks>
        /// <param name="deviceId">目标设备唯一标识</param>
        /// <param name="message">要发送的消息</param>
        /// <returns>发送是否成功</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>
        /// 异步向指定客户端发送消息
        /// </summary>
        /// <remarks>
        /// 使用帧格式发送消息(添加头尾标识)。
        /// 每个客户端的发送操作是互斥的(通过信号量实现)。
        /// 如果客户端未连接或不存在,发送失败返回 false。
        /// </remarks>
        /// <param name="clientId">目标客户端 ID</param>
        /// <param name="message">要发送的消息</param>
        /// <returns>发送是否成功</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>
        /// 将消息发送给所有已连接的客户端。
        /// 如果某个客户端发送失败,不影响其他客户端的发送。
        /// </remarks>
        /// <param name="message">要广播的消息</param>
        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>
        /// 通过 NetworkStream 发送消息
        /// </summary>
        /// <remarks>
        /// 直接使用 TcpClient 的 NetworkStream 发送消息。
        /// 消息会添加帧头帧尾。
        /// </remarks>
        /// <param name="client">TCP 客户端</param>
        /// <param name="message">消息内容</param>
        public 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);
        }
    }
}