Socket implementation of asynchronous TCPserver

Author: Xiaozhu zz address http://blog.csdn.net/zhujunxxxxx/article/details/44258719 Reprint please indicate the source

Socket asynchronous TCPserver I believe that people who have been engaged in network programming are not unfamiliar with TCP at all. In C, Microsoft has helped us encapsulate two classes: TcpListener and tcpcclient. The Socket encapsulation is implemented, but in fact, it is not very easy to use. So we use Socket to implement an asynchronous TCPserver.

In this article, I only give the server-side code. The client code can find other places. After all, I just want to write a good server

Here is the code

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net;

namespace NetFrame.Net.TCP.Sock.Asynchronous { /// <summary> ///Asynchronous TCP server implemented by Socket /// </summary> public class AsyncSocketTCPServer : IDisposable { #region Fields /// <summary> ///Maximum number of client connections agreed by server program /// </summary> private int _maxClient;

    /// <summary>
    ///Number of clients currently connected
    /// </summary>
    private int _clientCount;

    /// <summary>
    ///Asynchronous socket used by server
    /// </summary>
    private Socket _serverSock;

    /// <summary>
    ///Client session list
    /// </summary>
    private List<AsyncSocketState> _clients;

    private bool disposed = false;

    #endregion

    #region Properties

    /// <summary>
    ///Is the server executing
    /// </summary>
    public bool IsRunning { get; private set; }
    /// <summary>
    ///Monitored IP address
    /// </summary>
    public IPAddress Address { get; private set; }
    /// <summary>
    ///Listening port
    /// </summary>
    public int Port { get; private set; }
    /// <summary>
    ///Coding used in communication
    /// </summary>
    public Encoding Encoding { get; set; }


    #endregion

    #region constructor

    /// <summary>
    ///Asynchronous Socket TCP server
    /// </summary>
    ///< param name = "listenport" > listening port < / param >
    public AsyncSocketTCPServer(int listenPort)
        : this(IPAddress.Any, listenPort,1024)
    {
    }

    /// <summary>
    ///Asynchronous Socket TCP server
    /// </summary>
    ///< param name = "localep" > listening endpoint < / param >
    public AsyncSocketTCPServer(IPEndPoint localEP)
        : this(localEP.Address, localEP.Port,1024)
    {
    }

    /// <summary>
    ///Asynchronous Socket TCP server
    /// </summary>
    ///< param name = "localipaddress" > monitored IP address < / param >
    ///< param name = "listenport" > listening port < / param >
    ///< param name = "maxclient" > maximum number of clients < / param >
    public AsyncSocketTCPServer(IPAddress localIPAddress, int listenPort,int maxClient)
    {
        this.Address = localIPAddress;
        this.Port = listenPort;
        this.Encoding = Encoding.Default;

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

    #endregion

    #region Method

    /// <summary>
    ///Start server
    /// </summary>
    public void Start()
    {
        if (!IsRunning)
        {
            IsRunning = true;
            _serverSock.Bind(new IPEndPoint(this.Address, this.Port));
            _serverSock.Listen(1024);
            _serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock);
        }
    }

    /// <summary>
    ///Start server
    /// </summary>
    /// <param name="backlog">
    ///Maximum length of the pending connection sequence agreed by the server
    /// </param>
    public void Start(int backlog)
    {
        if (!IsRunning)
        {
            IsRunning = true;
            _serverSock.Bind(new IPEndPoint(this.Address, this.Port));
            _serverSock.Listen(backlog);
            _serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock);
        }
    }

    /// <summary>
    ///Stop server
    /// </summary>
    public void Stop()
    {
        if (IsRunning)
        {
            IsRunning = false;
            _serverSock.Close();
            //TODO closes the connection to all clients

        }
    }

    /// <summary>
    ///Handle client connections
    /// </summary>
    /// <param name="ar"></param>
    private void HandleAcceptConnected(IAsyncResult ar)
    {
        if (IsRunning)
        {
            Socket server = (Socket)ar.AsyncState;
            Socket client = server.EndAccept(ar);

            //Check if the maximum number of agreed clients is reached
            if (_clientCount >= _maxClient)
            {
                //C-TODO trigger event
                RaiseOtherException(null);
            }
            else
            {
                AsyncSocketState state = new AsyncSocketState(client);
                lock (_clients)
                {
                    _clients.Add(state);
                    _clientCount++;
                    RaiseClientConnected(state); //Trigger client connection event
                }
                state.RecvDataBuffer = new byte[client.ReceiveBufferSize];
                //Start accepting data from this client
                client.BeginReceive(state.RecvDataBuffer, 0, state.RecvDataBuffer.Length, SocketFlags.None,
                 new AsyncCallback(HandleDataReceived), state);
            }
            //Accept next request
            server.BeginAccept(new AsyncCallback(HandleAcceptConnected), ar.AsyncState);
        }
    }
    /// <summary>
    ///Processing client data
    /// </summary>
    /// <param name="ar"></param>
    private void HandleDataReceived(IAsyncResult ar)
    {
        if (IsRunning)
        {
            AsyncSocketState state = (AsyncSocketState)ar.AsyncState;
            Socket client = state.ClientSocket;
            try
            {
                //Suppose that asynchronous reception starts twice, so when the client exits
                //EndReceive will be executed twice
                int recv = client.EndReceive(ar);
                if (recv == 0)
                {
                    //C-todo trigger event (close client)
                    Close(state);
                    RaiseNetError(state);
                    return;
                }
                //TODO processes the read data ps: the data is in RecvDataBuffer of state

                //C-todo trigger data receiving event
                RaiseDataReceived(state);
            }
            catch (SocketException)
            {
                //C-todo exception handling
                RaiseNetError(state);
            }
            finally
            {
                //Continue to receive data from clients
                client.BeginReceive(state.RecvDataBuffer, 0, state.RecvDataBuffer.Length, SocketFlags.None,
                 new AsyncCallback(HandleDataReceived), state);
            }
        }
    }

    /// <summary>
    ///Send data
    /// </summary>
    ///< param name = "state" > client session receiving data < / param >
    ///< param name = "data" > data message < / param >
    public void Send(AsyncSocketState state, byte[] data)
    {
        RaisePrepareSend(state);
        Send(state.ClientSocket, data);
    }

    /// <summary>
    ///Send data to the specified client asynchronously
    /// </summary>
    ///< param name = "client" > client < / param >
    ///< param name = "data" > message < / 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>
    ///Send data completion processing function
    /// </summary>
    ///< param name = "ar" > target client socket < / param >
    private void SendDataEnd(IAsyncResult ar)
    {
        ((Socket)ar.AsyncState).EndSend(ar);
        RaiseCompletedSend(null);
    }
    #endregion

    #region event

    /// <summary>
    ///Connection established event with client
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> ClientConnected;
    /// <summary>
    ///Disconnected event from client
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> ClientDisconnected;

    /// <summary>
    ///Trigger client connection event
    /// </summary>
    /// <param name="state"></param>
    private void RaiseClientConnected(AsyncSocketState state)
    {
        if (ClientConnected != null)
        {
            ClientConnected(this, new AsyncSocketEventArgs(state));
        }
    }
    /// <summary>
    ///Trigger client disconnect event
    /// </summary>
    /// <param name="client"></param>
    private void RaiseClientDisconnected(Socket client)
    {
        if (ClientDisconnected != null)
        {
            ClientDisconnected(this, new AsyncSocketEventArgs("Disconnected"));
        }
    }

    /// <summary>
    ///Data event received
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> DataReceived;

    private void RaiseDataReceived(AsyncSocketState state)
    {
        if (DataReceived != null)
        {
            DataReceived(this, new AsyncSocketEventArgs(state));
        }
    }

    /// <summary>
    ///Events before sending data
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> PrepareSend;

    /// <summary>
    ///Events triggered before sending data
    /// </summary>
    /// <param name="state"></param>
    private void RaisePrepareSend(AsyncSocketState state)
    {
        if (PrepareSend != null)
        {
            PrepareSend(this, new AsyncSocketEventArgs(state));
        }
    }

    /// <summary>
    ///Data sending completion event
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> CompletedSend;
    
    /// <summary>
    ///Event triggering data sending completion
    /// </summary>
    /// <param name="state"></param>
    private void RaiseCompletedSend(AsyncSocketState state)
    {
        if (CompletedSend != null)
        {
            CompletedSend(this, new AsyncSocketEventArgs(state));
        }
    }

    /// <summary>
    ///Network error events
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> NetError;
    /// <summary>
    ///Trigger network error event
    /// </summary>
    /// <param name="state"></param>
    private void RaiseNetError(AsyncSocketState state)
    {
        if (NetError != null)
        {
            NetError(this, new AsyncSocketEventArgs(state));
        }
    }

    /// <summary>
    ///Abnormal events
    /// </summary>
    public event EventHandler<AsyncSocketEventArgs> OtherException;
    /// <summary>
    ///Trigger exception event
    /// </summary>
    /// <param name="state"></param>
    private void RaiseOtherException(AsyncSocketState state, string descrip)
    {
        if (OtherException != null)
        {
            OtherException(this, new AsyncSocketEventArgs(descrip, state));
        }
    }
    private void RaiseOtherException(AsyncSocketState state)
    {
        RaiseOtherException(state, "");
    }
    #endregion

    #region Close
    /// <summary>
    ///Close a session with a client
    /// </summary>
    ///< param name = "state" > client session object to be closed < / param >
    public void Close(AsyncSocketState state)
    {
        if (state != null)
        {
            state.Datagram = null;
            state.RecvDataBuffer = null;

            _clients.Remove(state);
            _clientCount--;
            //TODO trigger close event
            state.Close();
        }
    }
    /// <summary>
    ///Close all client sessions and disconnect all clients
    /// </summary>
    public void CloseAllClient()
    {
        foreach (AsyncSocketState client in _clients)
        {
            Close(client);
        }
        _clientCount = 0;
        _clients.Clear();
    }
    #endregion

    #region release
    /// <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)
                {
                    //TODO
                    RaiseOtherException(null);
                }
            }
            disposed = true;
        }
    }
    #endregion
}

}

Event parameter class using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace NetFrame.Net.TCP.Sock.Asynchronous { /// <summary> ///Asynchronous Socket TCP event parameter class /// </summary> public class AsyncSocketEventArgs:EventArgs { /// <summary> ///Tips /// </summary> public string _msg;

    /// <summary>
    ///client state encapsulation class
    /// </summary>
    public AsyncSocketState _state;

    /// <summary>
    ///Has it been handled
    /// </summary>
    public bool IsHandled { get; set; }

    public AsyncSocketEventArgs(string msg)
    {
        this._msg = msg;
        IsHandled = false;
    }
    public AsyncSocketEventArgs(AsyncSocketState state)
    {
        this._state = state;
        IsHandled = false;
    }
    public AsyncSocketEventArgs(string msg, AsyncSocketState state)
    {
        this._msg = msg;
        this._state = state;
        IsHandled = false;
    }
}

}

User status encapsulation using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets;

namespace NetFrame.Net.TCP.Sock.Asynchronous { /// <summary> ///Classes used to store client status information in asynchronous SOCKET TCP /// </summary> public class AsyncSocketState { #region field /// <summary> ///Receive data buffer /// </summary> private byte[] _recvBuffer;

    /// <summary>
    ///Message sent by client to server
    ///Note: in some cases, the message may be just a fragment of the message and not complete
    /// </summary>
    private string _datagram;

    /// <summary>
    ///Socket of client
    /// </summary>
    private Socket _clientSock;

    #endregion

    #region attribute

    /// <summary>
    ///Receive data buffer 
    /// </summary>
    public byte[] RecvDataBuffer
    {
        get
        {
            return _recvBuffer;
        }
        set
        {
            _recvBuffer = value;
        }
    }

    /// <summary>
    ///Message of access session
    /// </summary>
    public string Datagram
    {
        get
        {
            return _datagram;
        }
        set
        {
            _datagram = value;
        }
    }

    /// <summary>
    ///Get the Socket object associated with the client session
    /// </summary>
    public Socket ClientSocket
    {
        get
        {
            return _clientSock;

        }
    }


    #endregion

    /// <summary>
    ///Constructor
    /// </summary>
    ///< param name = "click" > Socket connection used by the session < / param >
    public AsyncSocketState(Socket cliSock)
    {
        _clientSock = cliSock;
    }

    /// <summary>
    ///Initialize data buffer
    /// </summary>
    public void InitBuffer()
    {
        if (_recvBuffer == null&&_clientSock!=null)
        {
            _recvBuffer=new byte[_clientSock.ReceiveBufferSize];
        }
    }

    /// <summary>
    ///Close session
    /// </summary>
    public void Close()
    {

        //Turn off receiving and sending of data
        _clientSock.Shutdown(SocketShutdown.Both);

        //Clean up resources
        _clientSock.Close();
    }
}

}

Tags: socket Session encoding network

Posted on Mon, 04 May 2020 11:40:50 -0400 by nosmasu