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 { /// /// 获取所有已连接客户端 ID 列表 /// /// /// 返回当前在服务器注册的客户端标识列表。 /// 这是一个只读列表的快照,线程安全。 /// /// 客户端 ID 列表 public IReadOnlyList GetClientIds() { lock (_syncRoot) { return _clients.Keys.ToList(); } } /// /// 根据设备 ID 获取客户端 ID /// /// /// 在设备绑定表中查找对应的客户端 ID。 /// /// 设备唯一标识 /// 客户端 ID,如果未找到则返回 null public string? GetClientIdByDevice(string deviceId) { lock (_syncRoot) { return _deviceBindings.TryGetValue(deviceId, out var cid) ? cid : null; } } /// /// 异步向指定设备发送消息 /// /// /// 通过设备 ID 查找对应的客户端连接,然后发送消息。 /// 如果设备未注册或连接不存在,返回 false。 /// /// 目标设备唯一标识 /// 要发送的消息 /// 发送是否成功 public Task SendToDeviceAsync(string deviceId, string message) { var clientId = GetClientIdByDevice(deviceId); if (clientId == null) return Task.FromResult(false); return SendToClientAsync(clientId, message); } /// /// 异步向指定客户端发送消息 /// /// /// 使用帧格式发送消息(添加头尾标识)。 /// 每个客户端的发送操作是互斥的(通过信号量实现)。 /// 如果客户端未连接或不存在,发送失败返回 false。 /// /// 目标客户端 ID /// 要发送的消息 /// 发送是否成功 public async Task 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; } /// /// 异步广播消息到所有客户端 /// /// /// 将消息发送给所有已连接的客户端。 /// 如果某个客户端发送失败,不影响其他客户端的发送。 /// /// 要广播的消息 public async Task BroadcastAsync(string message) { List clients; lock (_syncRoot) { clients = _clients.Values.ToList(); } // 并行发送消息到所有客户端 await Task.WhenAll(clients.Select(c => Task.Run(async () => { try { await SendMessageAsync(c, message); } catch { } }))); } /// /// 通过 NetworkStream 发送消息 /// /// /// 直接使用 TcpClient 的 NetworkStream 发送消息。 /// 消息会添加帧头帧尾。 /// /// TCP 客户端 /// 消息内容 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); } } }