c#:TCP服务端管理类

发布于:2025-08-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

TCP客户端连接多个服务端的类

1.架构图

在这里插入图片描述

2.创建TCP 客户端与服务端通信的工具类

注:TcpClientAsyncTool类中是客户端连接服务端的,TcpClient 实质是Server,套用服务端连接客户端的,使用过程中自行修改名称,本案例暂未修改。连接使用异步操作,其余为同步执行的。

    public class TcpClientAsyncTool
    {
        private TcpClient _tcpClient;
        public bool isConnected = false;
        public IPAddress IPAddress { get; private set; }
        private int _port;// 端口号
        private int _connectTimeout;// 连接超时时间(毫秒)
        private byte[] _receiveBuffer = new byte[1024];// 接收服务端数据

        /// <summary>
        /// 服务端连接信
        /// </summary>
        /// <param name="ip">ip地址</param>
        /// <param name="port">端口号</param>
        /// <param name="connectTimeout">连接超时时间</param>
        public TcpClientAsyncTool(string ip, int port, int connectTimeout = 2000)
        {
            _tcpClient = new TcpClient();
            this.IPAddress = IPAddress.Parse(ip);
            this._port = port;
            this._connectTimeout = connectTimeout;
        }

        /// <summary>
        /// 异步连接服务端
        /// </summary>
        /// <param name="connectDelegate"></param>
        /// <returns></returns>
        public async Task<(bool Success, string ErrorMsg)> ConnectAsync(Action connectDelegate = null)
        {
            if (_tcpClient.Connected)
            {
                isConnected = true;
                return (true, string.Empty);
            }

            try
            {
                var connectTask = Task.Factory.FromAsync(
                    _tcpClient.BeginConnect(IPAddress, _port, null, null),
                    _tcpClient.EndConnect);
                var timeoutTask = Task.Delay(_connectTimeout);
                var completedTask = await Task.WhenAny(connectTask, timeoutTask);

                if (completedTask == timeoutTask)
                {
                    try { _tcpClient.Close(); } catch { }
                    return (false, "连接超时");
                }
                await connectTask;

                if (!_tcpClient.Connected)
                {
                    return (false, "连接失败");
                }

                connectDelegate?.Invoke();
                isConnected = true;
                return (true, string.Empty);
            }
            catch (Exception ex)
            {
                isConnected = false;
                try { _tcpClient.Close(); } catch { }
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="errorMsg">错误信息</param>
        /// <returns>是否成功</returns>
        public bool DisConnect(out string errorMsg)
        {
            errorMsg = string.Empty;

            if (!_tcpClient.Connected)
            {
                isConnected = false;
                return true;
            }

            try
            {
                _tcpClient.Close();
                isConnected = false;
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                isConnected = false;
                return false;
            }
        }

        /// <summary>
        /// 同步发送数据
        /// </summary>
        /// <param name="sendBytes">要发送的字节数据</param>
        /// <param name="errorMsg">错误信息</param>
        /// <returns>是否发送成功</returns>
        public bool SendData(byte[] sendBytes, out string errorMsg)
        {
            errorMsg = string.Empty;

            if (!isConnected || !_tcpClient.Connected)
            {
                errorMsg = "客户端未连接";
                return false;
            }

            if (sendBytes == null || sendBytes.Length == 0)
            {
                errorMsg = "发送数据不能为空";
                return false;
            }

            try
            {
                NetworkStream networkStream = _tcpClient.GetStream();
                if (!networkStream.CanWrite)
                {
                    errorMsg = "网络流不可写";
                    return false;
                }

                IAsyncResult asyncResult = networkStream.BeginWrite(sendBytes, 0, sendBytes.Length, null, null);
                networkStream.EndWrite(asyncResult);
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                isConnected = false;
                try { _tcpClient.Close(); } catch { }
                return false;
            }
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="result">是否接收成功</param>
        /// <param name="errorMsg">错误信息</param>
        /// <returns>接收到的字节数据,如果失败返回null</returns>
        public byte[] ReceiveData(out bool result, out string errorMsg)
        {
            result = false;
            errorMsg = string.Empty;

            if (!isConnected || !_tcpClient.Connected)
            {
                errorMsg = "客户端未连接";
                return null;
            }

            try
            {
                NetworkStream networkStream = _tcpClient.GetStream();
                if (!networkStream.CanRead)
                {
                    errorMsg = "网络流不可读";
                    return null;
                }
                // 清空接收缓冲区
                Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);

                IAsyncResult asyncResult = networkStream.BeginRead(_receiveBuffer, 0, _receiveBuffer.Length, null, null);
                int readBytes = networkStream.EndRead(asyncResult);

                if (readBytes <= 0)
                {
                    isConnected = false;
                    errorMsg = "连接已关闭";
                    return null;
                }

                byte[] readByteArray = new byte[readBytes];
                Array.Copy(_receiveBuffer, readByteArray, readBytes);
                result = true;
                return readByteArray;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                isConnected = false;
                try { _tcpClient.Close(); } catch { }
                return null;
            }
        }
    }

3.创建类用于管理多个连接的服务端Server

    public class TcpClientManager : IDisposable
    {
        private readonly List<(string Ip, int Port)> _clientConfigurations;// 存储服务端配置的IP和端口
        private readonly ConcurrentDictionary<(string Ip, int Port), TcpClientAsyncTool> _clients =
        new ConcurrentDictionary<(string Ip, int Port), TcpClientAsyncTool>();//为服务端创建一个字典,存储IP和端口对应的TcpClientAsyncTool对象
        public event Action<string, byte[]> OnMessageReceivedWithIpPort;// 接收数据事件,包含IP和接收服务端返回的数据信息
        public event Action<string, bool> OnConnectionStateChanged; //设备连接状态变化事件
        private bool _disposed = false;// 确保Dispose方法只被调用一次

        public TcpClientManager(List<(string Ip, int Port)> clientConfigurations)
        {
            _clientConfigurations = clientConfigurations ?? throw new ArgumentNullException(nameof(clientConfigurations));
        }
        /// <summary>
        /// 初始化服务端连接
        /// </summary>
        /// <returns></returns>
        public async Task InitializeAsync()
        {
            var tasks = new List<Task>();
            foreach (var (ip, port) in _clientConfigurations)
            {
                tasks.Add(Task.Run(async () =>
                {
                    var client = new TcpClientAsyncTool(ip, port);
                    if (_clients.TryAdd((ip, port), client))
                    {
                        await ConnectToServer(ip, port);
                        _ = StartReceivingMessages(ip, port);
                    }
                }));
            }
            await Task.WhenAll(tasks);
        }

        public void SendCommandToServer(string ip, int port, byte[] byteData)
        {
            if (string.IsNullOrEmpty(ip))
                throw new ArgumentException("IP address cannot be null or empty.", nameof(ip));

            if (port <= 0 || port > 65535)
                throw new ArgumentException("Port number is invalid.", nameof(port));

            if (byteData == null)
                throw new ArgumentNullException(nameof(byteData));

            if (_clients.TryGetValue((ip, port), out var client))
            {
                string errorMsg = string.Empty;
                client.SendData(byteData, out errorMsg);
                if (!string.IsNullOrEmpty(errorMsg))
                {
                    Console.WriteLine($"Error sending data to {ip}:{port}: {errorMsg}");
                }
            }
            else
            {
                OnConnectionStateChanged?.Invoke(ip, false); // 触发断开事件
                Console.WriteLine($"Client {ip}:{port} is not connected.");
            }
        }
        /// <summary>
        /// 判断客户端与服务端是否连接
        /// </summary>
        /// <param name="ip">IP</param>
        /// <returns>返回连接状态</returns>
        /// <exception cref="ArgumentException"></exception>
        public bool IsDeviceConnected(string ip)
        {
            if (string.IsNullOrEmpty(ip))
                throw new ArgumentException("IP address cannot be null or empty.", nameof(ip));
            foreach (var client in _clients.Values)
            {
                if ((client.IPAddress).ToString() == (ip) && client.isConnected) 
                {
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// 服务端接收数据处理
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        private async Task StartReceivingMessages(string ip, int port)
        {
            try
            {
                while (_clients.TryGetValue((ip, port), out var client) && client.isConnected)
                {
                    try
                    {
                        bool result = false;
                        string errorMsg = string.Empty;
                        byte[] response = client.ReceiveData(out result, out errorMsg);

                        if (response != null && result)
                        {
                            OnMessageReceivedWithIpPort?.Invoke($"{ip}", response); //订阅接收数据事件,数据包含IP和端口
                        }
                        else if (!string.IsNullOrEmpty(errorMsg))
                        {
                            Console.WriteLine($"Receive error from {ip}:{port}: {errorMsg}");
                            break; // 退出循环,尝试重新连接
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Error receiving data from {ip}:{port}: {ex.Message}");
                        break; // 退出循环,尝试重新连接
                    }
                    await Task.Delay(10);
                }
            }
            finally
            {
                await Task.Delay(1000);
                _ = ConnectToServer(ip, port);
                _ = StartReceivingMessages(ip, port); 
            }
        }

        /// <summary>
        /// 异步连接服务端
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        private async Task ConnectToServer(string ip, int port)
        {
            if (_disposed) return;

            try
            {
                if (_clients.TryGetValue((ip, port), out var client))
                {
                    if (!client.isConnected)
                    {
                        await client.ConnectAsync();
                        OnConnectionStateChanged?.Invoke(ip, client.isConnected); // 触发断开事件
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error connecting to {ip}:{port}: {ex.Message}");
            }
        }
        /// <summary>
        /// 断开连接到服务端
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public void DisconnectToServer(string ip, int port)
        {
            if (_clients.TryRemove((ip, port), out var client))
            {
                string errorMsg = string.Empty;
                client.DisConnect(out errorMsg);
                OnConnectionStateChanged?.Invoke(ip, false); // 触发断开事件
                if (!string.IsNullOrEmpty(errorMsg))
                {
                    Console.WriteLine($"Error disconnecting {ip}:{port}: {errorMsg}");
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 关闭连接和释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // 释放托管资源
                    foreach (var client in _clients.Values)
                    {
                        try
                        {
                            string errorMsg = string.Empty;
                            client.DisConnect(out errorMsg);
                            if (!string.IsNullOrEmpty(errorMsg))
                            {
                                Console.WriteLine($"Error during disconnect: {errorMsg}");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Exception during client disposal: {ex.Message}");
                        }
                    }
                    _clients.Clear();
                }

                _disposed = true;
            }
        }

        ~TcpClientManager()
        {
            Dispose(false);
        }
    }