c#異步Socket Tcp服務器實現

jopen 9年前發布 | 5K 次閱讀 C#

使用的是TcpListener來實現的異步服務器

代碼

服務器核心代碼 AsyncServer.cs

    /// <summary>
/// 異步SOCKET 服務器
/// </summary>
public class AsyncServer : IDisposable
{

        #region Fields  
        /// <summary>  
        /// 服務器程序允許的最大客戶端連接數  
        /// </summary>  
        private int _maxClient;  

        /// <summary>  
        /// 當前的連接的客戶端數  
        /// </summary>  
        private int _clientCount;  

        /// <summary>  
        /// 服務器使用的異步socket  
        /// </summary>  
        private Socket _serverSock;  

        /// <summary>  
        /// 客戶端會話列表  
        /// </summary>  
        private List<Session> _clients;  

        private bool disposed = false;  

        #endregion  


        #region Properties  

        /// <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; }  
        /// <summary>  
        /// 通信使用的編碼  
        /// </summary>  
        public Encoding Encoding { get; set; }  


        #endregion  

        #region Ctors  

        /// <summary>  
        /// 異步Socket TCP服務器  
        /// </summary>  
        /// <param name="listenPort">監聽的端口</param>  
        public AsyncServer(int listenPort)  
            : this(IPAddress.Any, listenPort,1024)  
        {  
        }  

        /// <summary>  
        /// 異步Socket TCP服務器  
        /// </summary>  
        /// <param name="localEP">監聽的終結點</param>  
        public AsyncServer(IPEndPoint localEP)  
            : this(localEP.Address, localEP.Port,1024)  
        {  
        }  

        /// <summary>  
        /// 異步Socket TCP服務器  
        /// </summary>  
        /// <param name="localIPAddress">監聽的IP地址</param>  
        /// <param name="listenPort">監聽的端口</param>  
        /// <param name="maxClient">最大客戶端數量</param>  
        public AsyncServer(IPAddress localIPAddress, int listenPort,int maxClient)  
        {  
            this.Address = localIPAddress;  
            this.Port = listenPort;  
            this.Encoding = Encoding.Default;  

            _maxClient = maxClient;  
            _clients = new List<Session>();  
            _serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
        }  

        #endregion  


        #region Server  

        /// <summary>  
        /// 啟動服務器  
        /// </summary>  
        /// <returns>異步TCP服務器</returns>  
        public AsyncServer Start()  
        {  
            if (!IsRunning)  
            {  
                IsRunning = true;  
                _serverSock.Bind(new IPEndPoint(this.Address, this.Port));  
                _serverSock.Listen(1024);  
                _serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock);  
            }  
            return this;  
        }  

        /// <summary>  
        /// 啟動服務器  
        /// </summary>  
        /// <param name="backlog">  
        /// 服務器所允許的掛起連接序列的最大長度  
        /// </param>  
        /// <returns>異步TCP服務器</returns>  
        public AsyncServer Start(int backlog)  
        {  
            if (!IsRunning)  
            {  
                IsRunning = true;  
                _serverSock.Bind(new IPEndPoint(this.Address, this.Port));  
                _serverSock.Listen(backlog);  
                _serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock);  
            }  
            return this;  
        }  

        /// <summary>  
        /// 停止服務器  
        /// </summary>  
        /// <returns>異步TCP服務器</returns>  
        public AsyncServer Stop()  
        {  
            if (IsRunning)  
            {  
                IsRunning = false;  
                _serverSock.Close();  
                //TODO 關閉對所有客戶端的連接  

            }  
            return this;  
        }  

        #endregion  

        #region Receive  
        /// <summary>  
        /// 處理客戶端連接  
        /// </summary>  
        /// <param name="ar"></param>  
        private void HandleAcceptConnected(IAsyncResult ar)  
        {  
            if (IsRunning)  
            {  
                Socket server = (Socket)ar.AsyncState;  
                Socket client = server.EndAccept(ar);  

                //檢查是否達到最大的允許的客戶端數目  
                if (_clientCount == _maxClient)  
                {  
                    //TODO 觸發事件  
                    RaiseServerException(null);  
                }  
                else  
                {  
                    Session session = new Session(client);  
                    lock (_clients)  
                    {  
                        _clients.Add(session);  
                        _clientCount++;  
                        RaiseClientConnected(session); //觸發客戶端連接事件  
                    }  
                    session.RecvDataBuffer = new byte[client.ReceiveBufferSize];  
                    //開始接受來自該客戶端的數據  
                    client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,  
                     new AsyncCallback(HandleDataReceived), session);  
                }  
                //接受下一個請求  
                server.BeginAccept(new AsyncCallback(HandleAcceptConnected), ar.AsyncState);  
            }  
        }  
        /// <summary>  
        /// 處理客戶端數據  
        /// </summary>  
        /// <param name="ar"></param>  
        private void HandleDataReceived(IAsyncResult ar)  
        {  
            if (IsRunning)  
            {  
                Session session = (Session)ar.AsyncState;  
                Socket client = session.ClientSocket;  
                try  
                {  
                    //如果兩次開始了異步的接收,所以當客戶端退出的時候  
                    //會兩次執行EndReceive  
                    int recv = client.EndReceive(ar);  
                    if (recv == 0)  
                    {  
                        //TODO 觸發事件 (關閉客戶端)  
                        CloseSession(session);  
                        RaiseNetError(session);  
                        return;  
                    }  
                    //TODO 處理已經讀取的數據 ps:數據在session的RecvDataBuffer中  
                    RaiseDataReceived(session);  
                    //TODO 觸發數據接收事件  
                }  
                catch (SocketException ex)  
                {  
                    //TODO 異常處理  
                    RaiseNetError(session);  
                }  
                finally  
                {  
                    //繼續接收來自來客戶端的數據  
                    client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,  
                     new AsyncCallback(HandleDataReceived), session);  
                }  
            }  
        }  
        #endregion  

        #region Send  
        /// <summary>  
        /// 發送數據  
        /// </summary>  
        /// <param name="session">接收數據的客戶端會話</param>  
        /// <param name="data">數據報文</param>  
        public void Send(Session session, byte[] data)  
        {  
            Send(session.ClientSocket,data);  
        }  

        /// <summary>  
        /// 異步發送數據至指定的客戶端  
        /// </summary>  
        /// <param name="client">客戶端</param>  
        /// <param name="data">報文</param>  
        public void Send(Socket client, byte[] data)  
        {  
            if (!IsRunning)  
                throw new InvalidProgramException("This TCP Scoket server has not been started.");  

            if (client == null)  
                throw new ArgumentNullException("client");  

            if (data == null)  
                throw new ArgumentNullException("data");  
            client.BeginSend(data, 0, data.Length, SocketFlags.None,  
             new AsyncCallback(SendDataEnd), client);  
        }  

        /// <summary>  
        /// 發送數據完成處理函數  
        /// </summary>  
        /// <param name="ar">目標客戶端Socket</param>  
        private void SendDataEnd(IAsyncResult ar)  
        {  
            ((Socket)ar.AsyncState).EndSend(ar);  
        }  
        #endregion  

        #region Events  
        /// <summary>  
        /// 接收到數據事件  
        /// </summary>  
        public event EventHandler<EventArgs> DataReceived;  

        private void RaiseDataReceived(Session session)  
        {  
            if (DataReceived != null)  
            {  
                DataReceived(this, new AsyncEventArgs(session));  
            }  
        }  

        /// <summary>  
        /// 與客戶端的連接已建立事件  
        /// </summary>  
        public event EventHandler<AsyncEventArgs> ClientConnected;  
        /// <summary>  
        /// 與客戶端的連接已斷開事件  
        /// </summary>  
        public event EventHandler<AsyncEventArgs> ClientDisconnected;  

        /// <summary>  
        /// 觸發客戶端連接事件  
        /// </summary>  
        /// <param name="session"></param>  
        private void RaiseClientConnected(Session session)  
        {  
            if (ClientConnected != null)  
            {  
                ClientConnected(this, new AsyncEventArgs(session));  
            }  
        }  
        /// <summary>  
        /// 觸發客戶端連接斷開事件  
        /// </summary>  
        /// <param name="client"></param>  
        private void RaiseClientDisconnected(Socket client)  
        {  
            if (ClientDisconnected != null)  
            {  
                ClientDisconnected(this, new AsyncEventArgs("連接斷開"));  
            }  
        }  
        /// <summary>  
        /// 網絡錯誤事件  
        /// </summary>  
        public event EventHandler<AsyncEventArgs> NetError;  
        /// <summary>  
        /// 觸發網絡錯誤事件  
        /// </summary>  
        /// <param name="client"></param>  
        private void RaiseNetError(Session session)  
        {  
            if (NetError != null)  
            {  
                NetError(this, new AsyncEventArgs(session));  
            }  
        }  

        /// <summary>  
        /// 異常事件  
        /// </summary>  
        public event EventHandler<AsyncEventArgs> ServerException;  
        /// <summary>  
        /// 觸發異常事件  
        /// </summary>  
        /// <param name="client"></param>  
        private void RaiseServerException(Session session)  
        {  
            if (ServerException != null)  
            {  
                ServerException(this, new AsyncEventArgs(session));  
            }  
        }  
        #endregion  


        #region Close  
        /// <summary>  
        /// 關閉一個與客戶端之間的會話  
        /// </summary>  
        /// <param name="closeClient">需要關閉的客戶端會話對象</param>  
        public void CloseSession(Session session)  
        {  
            if (session != null)  
            {  
                session.Datagram = null;  
                session.RecvDataBuffer = null;  

                _clients.Remove(session);  
                _clientCount--;  
                //TODO 觸發關閉事件  
                session.Close();  
            }  
        }  
        /// <summary>  
        /// 關閉所有的客戶端會話,與所有的客戶端連接會斷開  
        /// </summary>  
        public void CloseAllClient()  
        {  
            foreach (Session client in _clients)  
            {  
                CloseSession(client);  
            }  
            _clientCount = 0;  
            _clients.Clear();  
        }  

        /// <summary>  
        /// Performs application-defined tasks associated with freeing,   
        /// releasing, or resetting unmanaged resources.  
        /// </summary>  
        public void Dispose()  
        {  
            Dispose(true);  
            GC.SuppressFinalize(this);  
        }  

        /// <summary>  
        /// Releases unmanaged and - optionally - managed resources  
        /// </summary>  
        /// <param name="disposing"><c>true</c> to release   
        /// both managed and unmanaged resources; <c>false</c>   
        /// to release only unmanaged resources.</param>  
        protected virtual void Dispose(bool disposing)  
        {  
            if (!this.disposed)  
            {  
                if (disposing)  
                {  
                    try  
                    {  
                        Stop();  
                        if (_serverSock != null)  
                        {  
                            _serverSock = null;  
                        }  
                    }  
                    catch (SocketException ex)  
                    {  
                        //TODO  
                        RaiseServerException(null);  
                    }  
                }  
                disposed = true;  
            }  
        }  
        #endregion  
    }  </pre> 


其中使用了一個Session類,來封裝對客戶端的連接

Session,cs

    /// <summary>
/// 客戶端與服務器之間的會話類
/// </summary>
public class Session
{

        #region 字段  
        /// <summary>  
        /// 接收數據緩沖區  
        /// </summary>  
        private byte[] _recvBuffer;  

        /// <summary>  
        /// 客戶端發送到服務器的報文  
        /// 注意:在有些情況下報文可能只是報文的片斷而不完整  
        /// </summary>  
        private string _datagram;  

        /// <summary>  
        /// 客戶端的Socket  
        /// </summary>  
        private Socket _clientSock;  

        #endregion  

        #region 屬性  

        /// <summary>  
        /// 接收數據緩沖區   
        /// </summary>  
        public byte[] RecvDataBuffer  
        {  
            get  
            {  
                return _recvBuffer;  
            }  
            set  
            {  
                _recvBuffer = value;  
            }  
        }  

        /// <summary>  
        /// 存取會話的報文  
        /// </summary>  
        public string Datagram  
        {  
            get  
            {  
                return _datagram;  
            }  
            set  
            {  
                _datagram = value;  
            }  
        }  

        /// <summary>  
        /// 獲得與客戶端會話關聯的Socket對象  
        /// </summary>  
        public Socket ClientSocket  
        {  
            get  
            {  
                return _clientSock;  

            }  
        }  


        #endregion  

        /// <summary>  
        /// 構造函數  
        /// </summary>  
        /// <param name="cliSock">會話使用的Socket連接</param>  
        public Session(Socket cliSock)  
        {  

            _clientSock = cliSock;  
        }  
        /// <summary>  
        /// 關閉會話  
        /// </summary>  
        public void Close()  
        {  

            //關閉數據的接受和發送  
            _clientSock.Shutdown(SocketShutdown.Both);  

            //清理資源  
            _clientSock.Close();  
        }  
    }  </pre> 


事件類

    class AsyncEventArgs : EventArgs
{
/// <summary>
/// 提示信息
/// </summary>
public string _msg;

        public Session _sessions;  

        /// <summary>  
        /// 是否已經處理過了  
        /// </summary>  
        public bool IsHandled { get; set; }  

        public AsyncEventArgs(string msg)  
        {  
            this._msg = msg;  
            IsHandled = false;  
        }  
        public AsyncEventArgs(Session session)  
        {  
            this._sessions = session;  
            IsHandled = false;  
        }  
        public AsyncEventArgs(string msg, Session session)  
        {  
            this._msg = msg;  
            this._sessions = session;  
            IsHandled = false;  
        }  
    }  </pre> 


來自:http://blog.csdn.net/zhujunxxxxx/article/details/43574655

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!