wanshenmean
7 天以前 37acb8358f5602a9013ee29c04a45e33483c2329
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
@@ -1,39 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.Text.Json;
using System.IO;
using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        public Task StartAsync(CancellationToken cancellationToken)
        /// <summary>
        /// 异步启动 TCP Socket 服务器
        /// </summary>
        /// <remarks>
        /// 创建 TCP 监听器并开始接受客户端连接。
        /// 如果服务器已在运行或被禁用,直接返回。
        /// 启动后启动接受循环和客户端监控任务。
        /// </remarks>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>启动任务</returns>
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            if (IsRunning || !_options.Enabled)
            {
                return Task.CompletedTask;
                return;
            }
            // 解析监听地址
            IPAddress ipAddress = IPAddress.Any;
            if (IPAddress.TryParse(_options.IpAddress, out IPAddress? parsedAddress))
            {
                ipAddress = parsedAddress;
            }
            await Task.Delay(5000);
            // 创建监听器
            _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;
            return;
        }
        /// <summary>
        /// 异步停止 TCP Socket 服务器
        /// </summary>
        /// <remarks>
        /// 停止接受新连接,等待所有客户端任务完成。
        /// </remarks>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>停止任务</returns>
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (!IsRunning)
@@ -41,9 +65,13 @@
                return;
            }
            // 发送取消信号
            _cts?.Cancel();
            // 停止监听
            _listener?.Stop();
            // 等待所有客户端任务完成
            Task[] tasks;
            lock (_syncRoot)
            {
@@ -58,6 +86,17 @@
            IsRunning = false;
        }
        /// <summary>
        /// 异步接受客户端连接的主循环
        /// </summary>
        /// <summary>
        /// 异步接受客户端连接的主循环
        /// </summary>
        /// <remarks>
        /// 在后台线程中持续接受新的客户端连接。
        /// 当有新连接时,将其添加到客户端字典并启动消息处理任务。
        /// </remarks>
        /// <param name="cancellationToken">取消令牌</param>
        private async Task AcceptLoopAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
@@ -65,30 +104,23 @@
                TcpClient? client = null;
                try
                {
                    // 等待客户端连接
                    client = await _listener!.AcceptTcpClientAsync().WaitAsync(cancellationToken);
                    QuartzLogger.Info($"客户端连接:{client.Client.RemoteEndPoint.ToString()}");
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (ObjectDisposedException)
                {
                    break;
                }
                catch (OperationCanceledException) { break; }
                catch (ObjectDisposedException) { break; }
                catch
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                    if (cancellationToken.IsCancellationRequested) break;
                }
                if (client == null)
                {
                    continue;
                }
                if (client == null) continue;
                // 生成客户端 ID(使用远程端点地址)
                string clientId = GetClientId(client);
                // 添加到客户端字典
                lock (_syncRoot)
                {
                    _clients[clientId] = client;
@@ -97,25 +129,42 @@
            }
        }
        /// <summary>
        /// 移除客户端连接
        /// </summary>
        /// <remarks>
        /// 关闭客户端连接并清理相关资源:
        /// - 关闭 TcpClient
        /// - 释放信号量
        /// - 移除活跃时间和编码记录
        /// - 移除设备绑定
        /// </remarks>
        /// <param name="clientId">要移除的客户端唯一标识</param>
        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)
                {
@@ -124,6 +173,14 @@
            }
        }
        /// <summary>
        /// 异步监控客户端空闲超时
        /// </summary>
        /// <remarks>
        /// 定期检查所有客户端的最后活跃时间,
        /// 如果超过空闲超时时间,断开该客户端连接。
        /// </remarks>
        /// <param name="cancellationToken">取消令牌</param>
        private async Task MonitorClientsAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
@@ -135,6 +192,7 @@
                    {
                        foreach (var kv in _clientLastActive)
                        {
                            // 检查是否超过空闲超时
                            if (_options.IdleTimeoutSeconds > 0 && DateTime.Now - kv.Value > TimeSpan.FromSeconds(_options.IdleTimeoutSeconds))
                            {
                                toRemove.Add(kv.Key);
@@ -142,20 +200,29 @@
                        }
                    }
                    // 断开超时的客户端
                    foreach (var cid in toRemove)
                    {
                        RemoveClient(cid);
                        Log($"[{DateTime.Now}] TcpSocketServer disconnect idle client {cid}");
                    }
                }
                catch
                {
                }
                catch { }
                // 每秒检查一次
                try { await Task.Delay(1000, cancellationToken); } catch { }
            }
        }
        /// <summary>
        /// 获取客户端唯一标识
        /// </summary>
        /// <remarks>
        /// 使用客户端的远程端点地址作为标识。
        /// 如果远程端点不可用,生成随机 GUID。
        /// </remarks>
        /// <param name="client">TCP 客户端</param>
        /// <returns>客户端标识字符串</returns>
        public static string GetClientId(TcpClient client)
        {
            return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString();