using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.IO;
using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_Tasks.SocketServer
{
public partial class TcpSocketServer
{
///
/// 异步启动 TCP Socket 服务器
///
///
/// 创建 TCP 监听器并开始接受客户端连接。
/// 如果服务器已在运行或被禁用,直接返回。
/// 启动后启动接受循环和客户端监控任务。
///
/// 取消令牌
/// 启动任务
public Task StartAsync(CancellationToken cancellationToken)
{
if (IsRunning || !_options.Enabled)
{
return Task.CompletedTask;
}
// 解析监听地址
IPAddress ipAddress = IPAddress.Any;
if (IPAddress.TryParse(_options.IpAddress, out IPAddress? parsedAddress))
{
ipAddress = parsedAddress;
}
// 创建监听器
_listener = new TcpListener(ipAddress, _options.Port);
_listener.Start(_options.Backlog);
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
IsRunning = true;
// 启动接受客户端连接循环
_ = AcceptLoopAsync(_cts.Token);
// 启动客户端监控任务(检查空闲超时)
_monitorTask = Task.Run(() => MonitorClientsAsync(_cts.Token));
return Task.CompletedTask;
}
///
/// 异步停止 TCP Socket 服务器
///
///
/// 停止接受新连接,等待所有客户端任务完成。
///
/// 取消令牌
/// 停止任务
public async Task StopAsync(CancellationToken cancellationToken)
{
if (!IsRunning)
{
return;
}
// 发送取消信号
_cts?.Cancel();
// 停止监听
_listener?.Stop();
// 等待所有客户端任务完成
Task[] tasks;
lock (_syncRoot)
{
tasks = _clientTasks.ToArray();
}
if (tasks.Length > 0)
{
await Task.WhenAll(tasks);
}
IsRunning = false;
}
///
/// 异步接受客户端连接的主循环
///
///
/// 异步接受客户端连接的主循环
///
///
/// 在后台线程中持续接受新的客户端连接。
/// 当有新连接时,将其添加到客户端字典并启动消息处理任务。
///
/// 取消令牌
private async Task AcceptLoopAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
TcpClient? client = null;
try
{
// 等待客户端连接
client = await _listener!.AcceptTcpClientAsync().WaitAsync(cancellationToken);
QuartzLogger.Info($"客户端连接:{client.Client.RemoteEndPoint.ToString()}");
}
catch (OperationCanceledException) { break; }
catch (ObjectDisposedException) { break; }
catch
{
if (cancellationToken.IsCancellationRequested) break;
}
if (client == null) continue;
// 生成客户端 ID(使用远程端点地址)
string clientId = GetClientId(client);
// 添加到客户端字典
lock (_syncRoot)
{
_clients[clientId] = client;
_clientLocks[clientId] = new SemaphoreSlim(1, 1);
}
}
}
///
/// 移除客户端连接
///
///
/// 关闭客户端连接并清理相关资源:
/// - 关闭 TcpClient
/// - 释放信号量
/// - 移除活跃时间和编码记录
/// - 移除设备绑定
///
/// 要移除的客户端唯一标识
private void RemoveClient(string clientId)
{
lock (_syncRoot)
{
// 关闭并移除客户端连接
if (_clients.TryGetValue(clientId, out var client))
{
try { client.Close(); } catch { }
_clients.Remove(clientId);
}
// 释放信号量
if (_clientLocks.TryGetValue(clientId, out var sem))
{
_clientLocks.Remove(clientId);
sem.Dispose();
}
// 移除活跃时间记录
_clientLastActive.Remove(clientId);
// 移除编码记录
_clientEncodings.Remove(clientId);
// 移除设备绑定
var deviceIds = _deviceBindings.Where(kv => kv.Value == clientId).Select(kv => kv.Key).ToList();
foreach (var deviceId in deviceIds)
{
_deviceBindings.Remove(deviceId);
}
}
}
///
/// 异步监控客户端空闲超时
///
///
/// 定期检查所有客户端的最后活跃时间,
/// 如果超过空闲超时时间,断开该客户端连接。
///
/// 取消令牌
private async Task MonitorClientsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
List toRemove = new();
lock (_syncRoot)
{
foreach (var kv in _clientLastActive)
{
// 检查是否超过空闲超时
if (_options.IdleTimeoutSeconds > 0 && DateTime.Now - kv.Value > TimeSpan.FromSeconds(_options.IdleTimeoutSeconds))
{
toRemove.Add(kv.Key);
}
}
}
// 断开超时的客户端
foreach (var cid in toRemove)
{
RemoveClient(cid);
Log($"[{DateTime.Now}] TcpSocketServer disconnect idle client {cid}");
}
}
catch { }
// 每秒检查一次
try { await Task.Delay(1000, cancellationToken); } catch { }
}
}
///
/// 获取客户端唯一标识
///
///
/// 使用客户端的远程端点地址作为标识。
/// 如果远程端点不可用,生成随机 GUID。
///
/// TCP 客户端
/// 客户端标识字符串
public static string GetClientId(TcpClient client)
{
return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString();
}
}
}