TCP服务器端帮助类

176 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

 public class MyEventArgsBase : EventArgs
    {
        /// <summary>
        /// 客户端
        /// </summary>
        public TcpClient TcpClient { get; set; }

        /// <summary>
        /// ToString方法
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"<ClientRemoteEndPoint:{TcpClient.Client.RemoteEndPoint}>";
        }
    }
  public class TcpConnectedEventArgs : MyEventArgsBase
    {
        /// <summary>
        /// 与客户端的连接已建立事件参数
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        public TcpConnectedEventArgs(TcpClient tcpClient)
        {
            TcpClient = tcpClient ?? throw new ArgumentNullException("tcpClient is null");
        }
    }
    public class TcpDisconnectedEventArgs : MyEventArgsBase
    {
        /// <summary>
        /// 与客户端的连接已断开事件参数
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        public TcpDisconnectedEventArgs(TcpClient tcpClient)
        {
            TcpClient = tcpClient ?? throw new ArgumentNullException("tcpClient is null");
        }
    }
    public class TcpMsgReceivedEventArgs<T> : MyEventArgsBase
    {
        /// <summary>
        /// 接收到数据报文事件参数
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="msg">报文</param>
        /// <param name="encoding">报文编码方式</param>
        public TcpMsgReceivedEventArgs(TcpClient tcpClient, T msg, Encoding encoding)
        {
            TcpClient = tcpClient;
            Msg = msg;
            Encoding = encoding;
        }

        /// <summary>
        /// 报文
        /// </summary>
        public T Msg { get; private set; }

        /// <summary>
        /// 报文编码方式
        /// </summary>
        public Encoding Encoding { get; private set; }
    }
  internal class TcpClientState
    {
        public TcpClientState(TcpClient tcpClient, byte[] buffer)
        {
            TcpClient = tcpClient ?? throw new ArgumentNullException("tcpClient is null");
            Buffer = buffer ?? throw new ArgumentNullException("buffer is null");
        }

        public TcpClient TcpClient { get; private set; }

        public byte[] Buffer { get; private set; }

        public NetworkStream NetworkStream
        {
            get { return TcpClient.GetStream(); }
        }
    }
    /// <summary>
    /// TCP服务器端帮助类
    /// </summary>
    public class TcpServerUtil
    {
        #region 事件

        /// <summary>
        /// 接收到报文事件
        /// </summary>
        public event EventHandler<TcpMsgReceivedEventArgs<byte[]>> ReceiveMsg;

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event EventHandler<TcpConnectedEventArgs> ClientConnected;

        /// <summary>
        /// 与客户端的连接已断开事件
        /// </summary>
        public event EventHandler<TcpDisconnectedEventArgs> ClientDisconnected;

        #endregion 事件

        #region 属性、字段

        private TcpListener listener;
        private List<TcpClientState> clients;
        private bool disposed = false;

        /// <summary>
        /// 服务器是否正在运行
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 监听的IP地址
        /// </summary>
        public IPAddress Address { get; private set; }

        /// <summary>
        /// 监听的端口
        /// </summary>
        public int Port { get; private set; }

        private Encoding _encoding;

        /// <summary>
        /// 通信使用的编码
        /// </summary>
        public Encoding Encoding
        { get { return _encoding; } set { _encoding = value; } }

        #endregion 属性、字段

        #region 方法

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="listenPort">监听的端口</param>
        public TcpServerUtil(int listenPort) : this(IPAddress.Any, listenPort)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localEP">监听的终结点</param>
        public TcpServerUtil(IPEndPoint localEP) : this(localEP.Address, localEP.Port)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localIPAddress">监听的IP地址</param>
        /// <param name="listenPort">监听的端口</param>
        public TcpServerUtil(IPAddress localIPAddress, int listenPort)
        {
            Address = localIPAddress;
            Port = listenPort;
            Encoding = Encoding.UTF8;//.Default;
            clients = new List<TcpClientState>();
            listener = new TcpListener(Address, Port);
            listener.AllowNatTraversal(true);
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <returns>异步TCP服务器</returns>
        public TcpServerUtil Start()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                listener.Start();
                listener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), listener);
            }
            return this;
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="backlog">
        /// 服务器所允许的挂起连接序列的最大长度
        /// </param>
        /// <returns>异步TCP服务器</returns>
        public TcpServerUtil Start(int backlog)
        {
            if (!IsRunning)
            {
                IsRunning = true;
                listener.Start(backlog);
                listener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), listener);
            }
            return this;
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        /// <returns>异步TCP服务器</returns>
        public TcpServerUtil Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;
                listener.Stop();
                lock (clients)
                {
                    foreach (var item in clients)
                    {
                        item.TcpClient.Client.Disconnect(false);
                    }
                    clients.Clear();
                }
            }
            return this;
        }

        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            if (IsRunning)
            {
                TcpListener tcpListener = (TcpListener)ar.AsyncState;
                TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar);
                byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
                TcpClientState internalClient = new TcpClientState(tcpClient, buffer);
                lock (clients)
                {
                    clients.Add(internalClient);
                    ClientConnected?.Invoke(this, new TcpConnectedEventArgs(tcpClient));
                }
                NetworkStream networkStream = internalClient.NetworkStream;
                networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, HandleDatagramReceived, internalClient);
                tcpListener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState);
            }
        }

        private void HandleDatagramReceived(IAsyncResult ar)
        {
            if (IsRunning)
            {
                TcpClientState internalClient = (TcpClientState)ar.AsyncState;
                NetworkStream networkStream = internalClient.NetworkStream;
                int numberOfReadBytes = 0;
                try
                {
                    numberOfReadBytes = networkStream.EndRead(ar);
                }
                catch
                {
                    numberOfReadBytes = 0;
                }

                if (numberOfReadBytes == 0)
                {
                    // connection has been closed
                    lock (clients)
                    {
                        clients.Remove(internalClient);
                        ClientDisconnected?.BeginInvoke(this, new TcpDisconnectedEventArgs(internalClient.TcpClient), null, null);
                        return;
                    }
                }

                // received byte and trigger event notification
                byte[] receivedBytes = new byte[numberOfReadBytes];
                Buffer.BlockCopy(internalClient.Buffer, 0, receivedBytes, 0, numberOfReadBytes);
                ReceiveMsg?.BeginInvoke(this, new TcpMsgReceivedEventArgs<byte[]>(internalClient.TcpClient, receivedBytes, _encoding), null, null);
                networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, HandleDatagramReceived, internalClient);// continue listening for tcp datagram packets
            }
        }

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="msg">报文</param>
        public void Send(TcpClient tcpClient, byte[] msg)
        {
            if (!IsRunning)
                throw new InvalidProgramException("This TCP server has not been started.");

            if (tcpClient == null)
                throw new ArgumentNullException("tcpClient is null");

            if (msg == null)
                throw new ArgumentNullException("msg is null");

            tcpClient.GetStream().BeginWrite(msg, 0, msg.Length, (IAsyncResult ar) => ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar), tcpClient);
        }

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="msg">报文</param>
        public void Send(TcpClient tcpClient, string msg)
        {
            Send(tcpClient, Encoding.GetBytes(msg));
        }

        /// <summary>
        /// 发送报文至所有客户端
        /// </summary>
        /// <param name="msg"></param>
        public void SendAll(string msg)
        {
            if (!IsRunning)
                throw new InvalidProgramException("This TCP server has not been started.");

            for (int i = 0; i < clients.Count; i++)
            {
                Send(clients[i].TcpClient, msg);
            }
        }

        /// <summary>
        /// Dispose方法
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        ~TcpServerUtil()
        {
            Dispose(false);
        }

        /// <summary>
        /// Dispose方法
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    Stop();

                    if (listener != null)
                    {
                        listener = null;
                    }
                }

                disposed = true;
            }
        }

        #endregion 方法
    }