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