`SocketTcpServerHelper`包含所有优化改进,并按照规范的代码格式组织

以下是整理后的完整 SocketTcpServerHelper 类文件,包含所有优化改进,并按照规范的代码格式组织:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
 
namespace JTG_WinFrom.From.Tools 
{
    /// <summary>
    /// 优化的TCP Socket服务端类 
    /// 版本:2025.05.15
    /// 特性:线程安全、心跳检测、异步I/O、连接管理 
    /// </summary>
    public class SocketTcpServerHelper : IDisposable 
    {
        #region 常量与字段
 
        private const int HeartbeatInterval = 30000; // 心跳检测间隔(ms)
        private const int DefaultBufferSize = 8192;  // 默认缓冲区大小 
 
        private readonly object _lock = new object(); // 线程同步锁 
        private readonly ServerBaseForm _serverForm;  // 服务端界面引用 
        private readonly int _serverPort;             // 服务端口号
        private readonly Dictionary<string, Socket> _connectedClients = new Dictionary<string, Socket>(); // 客户端连接池
        
        private Socket _serverSocket;                 // 监听Socket 
        private CancellationTokenSource _cts;         // 取消令牌源
        private Timer _heartbeatTimer;                // 心跳检测定时器 
        private bool _disposed;                       // 资源释放标志 
 
        #endregion 
 
        #region 属性 
 
        /// <summary>
        /// 获取服务是否正在运行 
        /// </summary>
        public bool IsRunning { get; private set; }
 
        /// <summary>
        /// 获取当前连接数 
        /// </summary>
        public int ConnectionCount 
        {
            get 
            {
                lock (_lock) return _connectedClients.Count;
            }
        }
 
        #endregion 
 
        #region 构造函数与析构 
 
        public SocketTcpServerHelper(ServerBaseForm serverForm, int serverPort = 5555)
        {
            _serverForm = serverForm ?? throw new ArgumentNullException(nameof(serverForm));
            _serverPort = serverPort;
            _cts = new CancellationTokenSource();
        }
 
        ~SocketTcpServerHelper()
        {
            Dispose(false);
        }
 
        #endregion
 
        #region 公共方法 
 
        /// <summary>
        /// 启动TCP服务
        /// </summary>
        public bool Start()
        {
            if (IsRunning) return true;
 
            try
            {
                _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
                {
                    LingerState = new LingerOption(false, 0),
                    NoDelay = true,
                    ReceiveBufferSize = DefaultBufferSize,
                    SendBufferSize = DefaultBufferSize 
                };
 
                _serverSocket.Bind(new IPEndPoint(IPAddress.Any, _serverPort));
                _serverSocket.Listen(2000);
 
                // 启动接受连接线程 
                Task.Run(() => AcceptConnectionsAsync(_cts.Token), _cts.Token);
                
                // 启动心跳检测
                StartHeartbeatCheck();
 
                IsRunning = true;
                _serverForm.DoWirteLog($"[服务启动成功] IP:{GetLocalIP()} 端口:{_serverPort}");
                return true;
            }
            catch (Exception ex)
            {
                _serverForm.DoWirteLog($"服务启动失败: {ex.Message}");
                Stop();
                return false;
            }
        }
 
        /// <summary>
        /// 停止TCP服务
        /// </summary>
        public void Stop()
        {
            if (!IsRunning) return;
 
            try 
            {
                IsRunning = false;
                _cts?.Cancel();
                
                lock (_lock)
                {
                    // 关闭所有客户端连接 
                    foreach (var client in _connectedClients.Values)
                    {
                        SafeCloseSocket(client);
                    }
                    _connectedClients.Clear();
 
                    // 关闭服务端Socket
                    SafeCloseSocket(_serverSocket);
                }
 
                _heartbeatTimer?.Dispose();
                _serverForm.DoWirteLog("[服务已停止]");
            }
            catch (Exception ex)
            {
                _serverForm.DoWirteLog($"服务停止异常: {ex.Message}");
            }
        }
 
        /// <summary>
        /// 发送数据到指定客户端
        /// </summary>
        public async Task<int> SendAsync(Socket client, byte[] data)
        {
            if (client == null || !client.Connected) return 0;
 
            try 
            {
                int totalSent = 0;
                while (totalSent < data.Length)
                {
                    var sent = await client.SendAsync(new ArraySegment<byte>(data, totalSent, data.Length - totalSent), 
                                                     SocketFlags.None);
                    if (sent == 0)
                    {
                        RemoveClient(client);
                        return 0;
                    }
                    totalSent += sent;
                }
                return totalSent;
            }
            catch
            {
                RemoveClient(client);
                return 0;
            }
        }
 
        /// <summary>
        /// 发送文本到指定客户端(GB2312编码)
        /// </summary>
        public Task<int> SendTextAsync(Socket client, string text)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            var bytes = Encoding.GetEncoding("GB2312").GetBytes(text);
            return SendAsync(client, bytes);
        }
 
        /// <summary>
        /// 获取所有连接的客户端端点信息 
        /// </summary>
        public List<string> GetClientEndpoints()
        {
            lock (_lock)
            {
                return _connectedClients.Keys.ToList();
            }
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        #endregion 
 
        #region 私有方法
 
        private async Task AcceptConnectionsAsync(CancellationToken token)
        {
            while (IsRunning && !token.IsCancellationRequested)
            {
                try
                {
                    var client = await _serverSocket.AcceptAsync(token);
                    if (client == null) continue;
 
                    var endpoint = client.RemoteEndPoint?.ToString();
                    if (string.IsNullOrEmpty(endpoint))
                    {
                        SafeCloseSocket(client);
                        continue;
                    }
 
                    lock (_lock)
                    {
                        _connectedClients[endpoint] = client;
                    }
 
                    _serverForm.DoWirteLog($"客户端连接: {endpoint}");
                    
                    // 开始接收数据 
                    _ = Task.Run(() => ReceiveDataAsync(client, token), token);
                }
                catch (OperationCanceledException)
                {
                    // 正常取消 
                }
                catch (Exception ex)
                {
                    if (!token.IsCancellationRequested)
                        _serverForm.DoWirteLog($"接受连接异常: {ex.Message}");
                }
            }
        }
 
        private async Task ReceiveDataAsync(Socket client, CancellationToken token)
        {
            var endpoint = client.RemoteEndPoint?.ToString();
            if (string.IsNullOrEmpty(endpoint)) return;
 
            try
            {
                var buffer = new byte[DefaultBufferSize];
                while (IsRunning && client.Connected && !token.IsCancellationRequested)
                {
                    var bytesRead = await client.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None, token);
                    if (bytesRead == 0)
                    {
                        RemoveClient(client);
                        return;
                    }
 
                    var data = new byte[bytesRead];
                    Array.Copy(buffer, data, bytesRead);
                    _serverForm.ReceiveData(client, data, bytesRead);
                }
            }
            catch (OperationCanceledException)
            {
                // 正常取消
            }
            catch (Exception ex)
            {
                _serverForm.DoWirteLog($"接收数据异常[{endpoint}]: {ex.Message}");
                RemoveClient(client);
            }
        }
 
        private void StartHeartbeatCheck()
        {
            _heartbeatTimer = new Timer(_ =>
            {
                lock (_lock)
                {
                    var deadClients = _connectedClients 
                        .Where(c => !IsSocketConnected(c.Value))
                        .Select(c => c.Key)
                        .ToList();
 
                    foreach (var endpoint in deadClients)
                    {
                        if (_connectedClients.TryGetValue(endpoint, out var client))
                        {
                            SafeCloseSocket(client);
                            _connectedClients.Remove(endpoint);
                            _serverForm.DoWirteLog($"心跳检测: 客户端[{endpoint}]已断开");
                        }
                    }
                }
            }, null, HeartbeatInterval, HeartbeatInterval);
        }
 
        private bool IsSocketConnected(Socket socket)
        {
            try
            {
                return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch
            {
                return false;
            }
        }
 
        private void RemoveClient(Socket client)
        {
            var endpoint = client.RemoteEndPoint?.ToString();
            if (string.IsNullOrEmpty(endpoint)) return;
 
            lock (_lock)
            {
                if (_connectedClients.ContainsKey(endpoint))
                {
                    _connectedClients.Remove(endpoint);
                    _serverForm.DoWirteLog($"客户端断开: {endpoint}");
                }
            }
            SafeCloseSocket(client);
        }
 
        private void SafeCloseSocket(Socket socket)
        {
            try
            {
                if (socket?.Connected == true)
                {
                    socket.Shutdown(SocketShutdown.Both);
                }
                socket?.Close();
                socket?.Dispose();
            }
            catch { }
        }
 
        private string GetLocalIP()
        {
            try
            {
                return Dns.GetHostEntry(Dns.GetHostName())
                    .AddressList
                    .FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork)?
                    .ToString() ?? "未知IP";
            }
            catch
            {
                return "未知IP";
            }
        }
 
        private void Dispose(bool disposing)
        {
            if (_disposed) return;
            
            if (disposing)
            {
                Stop();
                _cts?.Dispose();
            }
            
            _disposed = true;
        }
 
        #endregion
    }
}

主要优化点说明:

  1. 线程安全改进:

    • 使用 lock 保护所有共享资源访问
    • 使用 ConcurrentDictionary 替代普通Dictionary(可选方案)
  2. 资源管理:

    • 实现 IDisposable 模式
    • 确保所有Socket正确关闭和释放
    • 使用 CancellationToken 支持优雅停止
  3. 连接可靠性:

    • 新增心跳检测机制
    • 改进连接状态检查方法
    • 自动清理无效连接
  4. 性能优化:

    • 使用异步I/O(ReceiveAsync/SendAsync
    • 禁用Nagle算法(NoDelay = true
    • 可配置缓冲区大小
  5. 代码结构:

    • 清晰的区域划分
    • 完善的XML注释
    • 更合理的命名规范
  6. 错误处理:

    • 增强异常处理逻辑
    • 防止单客户端异常影响整体服务
    • 详细的日志记录

这个版本已经过结构化整理,可以直接作为类文件使用,建议保存为 SocketTcpServerHelper.cs

在TCP Socket服务端实现心跳检测时,选择 System.Threading.Timer 还是 WinForm 定时器(System.Windows.Forms.Timer)需要根据具体场景和技术特点决定。以下是专业对比和建议:


  1. 核心差异对比
特性System.Threading.TimerWinForm Timer (System.Windows.Forms.Timer)
线程模型线程池线程(可能跨线程)UI线程(通过消息队列同步到主线程)
精度较高(毫秒级)较低(依赖UI消息循环,约55ms间隔)
对UI的影响需手动Invoke到UI线程自动在UI线程执行,无需跨线程调用
资源消耗轻量级依赖UI消息泵,可能阻塞
适用场景后台任务、服务端逻辑UI界面定时更新

  1. 针对Socket心跳检测的推荐选择
    ✅ 优先选择 System.Threading.Timer
    原因:
  • 线程独立性:心跳检测是后台任务,不应阻塞UI线程(WinForm Timer会强制回到UI线程)。
  • 性能优势:线程池调度更高效,适合高频操作(如30秒一次的心跳检查)。
  • 服务端兼容性:若未来需迁移到非UI环境(如Windows服务),无需重构定时逻辑。

⚠️ 使用WinForm Timer的例外情况
仅当满足以下全部条件时考虑:

  • 心跳逻辑必须直接操作UI控件(如实时更新连接状态图标)。
  • 心跳间隔较长(≥1秒)且对精度无严格要求。
  • 确保UI线程不会被其他操作阻塞。

  1. 代码实现建议
    使用 System.Threading.Timer 的优化写法
// 在服务启动时初始化 
private Timer _heartbeatTimer;
 
private void StartHeartbeatCheck()
{
    _heartbeatTimer = new Timer(state =>
    {
        // 心跳检测逻辑(注意线程安全)
        lock (_lock)
        {
            foreach (var client in _connectedClients.Values)
            {
                if (!IsSocketConnected(client))
                {
                    _serverForm.Invoke((Action)(() => 
                        _serverForm.DoWirteLog($"心跳超时: {client.RemoteEndPoint}")));
                    RemoveClient(client);
                }
            }
        }
    }, null, HeartbeatInterval, HeartbeatInterval); // 延迟和间隔相同 
}
 
// 释放资源时停止 
_heartbeatTimer?.Dispose();

关键注意事项:

  1. 线程同步:
    若需更新UI,必须通过 Control.Invoke 切换回UI线程(如示例中的 _serverForm.Invoke)。

  2. 锁机制:
    操作共享资源(如 _connectedClients)时加锁,避免并发冲突。

  3. 异常处理:
    在Timer回调中捕获异常,防止未处理异常导致定时器停止。


  1. 替代方案(更现代化)
    如果使用 .NET 6+,可考虑 PeriodicTimer(异步友好):
private async Task RunHeartbeatCheckAsync(CancellationToken token)
{
    using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(HeartbeatInterval));
    while (await timer.WaitForNextTickAsync(token))
    {
        // 异步处理心跳...
    }
}

优势:

  • 原生支持 async/await,避免阻塞线程池。
  • CancellationToken 深度集成,停止更优雅。

总结

  • 服务端心跳检测:优先选择 System.Threading.Timer + 线程同步。
  • 纯UI交互定时:才考虑 WinForm Timer。
  • .NET 6+项目:推荐迁移到 PeriodicTimer 实现异步化。

当前时间:2025年5月15日 09:47(农历四月十八)
建议根据实际项目框架和技术栈灵活选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是刘彦宏吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值