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);
}
}
}