Home > Networking > Dotnet Async Socket Server – 3.Bare bone connection

Dotnet Async Socket Server – 3.Bare bone connection

 

In this part, I will implement the bare bone socket connection with console apps.

 

Contents of this series

 

Overview

It’s good to review the connection diagram in the prior architecture part for understanding this implementation.

 

 

Let’s start a console socket client and a console socket server with connection functions. Check the solution. This contains 1 library and 2 console projects.

 

Click this image to see large class diagram.

 

The base of the Socket Listener class

Let us check the core members of the class. I use 4 action delegates to fire async events instead of event handlers or general delegates.

    public class SocketListener
    {
        private readonly int backlog;
        private readonly int bufferSize;
        private IPEndPoint endPoint;
        private Socket listenSocket;
        private SocketAsyncEventArgs acceptArgs;

        public Action<Socket> OnStarted;
        public Action<IPEndPoint> OnStopped;
        public Action<Socket> OnConnected;
        public Action<EndPoint> OnDisconnected;
        ...

 

In the constructor, the acceptArgs object should be instantiated. It is important to assign a event handler for the accpetArgs’ callback operation. We should implement Accept_Completed handler.

        public SocketListener(IPAddress address,int port,int backlog,int bufferSize)
        {
            ...

            this.acceptArgs = new SocketAsyncEventArgs();
            this.acceptArgs.Completed += new EventHandler<SocketAsyncEventArgs>(Accept_Completed);
        }
        ...

        private void Accept_Completed(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessAccept(e);
        }

 

These public methods are for starting and stopping the server. If the OnStared and OnStpped actions be not implemented by the console server that uses this listener class, nothing would be happened the server.

        public bool IsRunning()
        {
            return this.listenSocket != null;
        }

        public void StartListener()
        {
            if (IsRunning())
            {
                 throw new InvalidOperationException("The Server is already running.");
            }

            this.listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.listenSocket.Bind(endPoint);
            this.listenSocket.Listen(backlog);

            if (OnStarted != null)
            {
                OnStarted(this.listenSocket);
            }

            this.StartAccept();
        }

        public void StopListener()
        {
            if (listenSocket == null) return;
            listenSocket.Close();
            listenSocket = null;

            if (OnStopped != null)
            {
                OnStopped(this.endPoint);
            }
        }

 

Accepting methods

If the StartListener method be called, this StartAccept method might be operated. Also this method will be called again by the ProcessAccept method after one acceptance(connection)’s completion.

        private void StartAccept()
        {
            if (this.acceptArgs.AcceptSocket != null)
            {
                this.acceptArgs.AcceptSocket = null;
            }

            if (!this.listenSocket.AcceptAsync(this.acceptArgs))
            {
                this.ProcessAccept(this.acceptArgs);
            }
        }

 

Be aware of the ProcessAccept method. We must pass the OnDisconnected action of this listener to the connection class because we can not catch disconnect event in this current listener. This disconnected event can be fired in the Connection class with messaging functions.

 

Implementing the Connection class

Let’s make the Connection constructor and a completion callback method for the messageArgs.

    public class Connection
    {
        private Socket socket;
        private EndPoint endPoint;
        private SocketAsyncEventArgs messageArgs;

        private Action<EndPoint> onDisconnected;

        public Connection(Socket socket,Action<EndPoint> onDisconnected, SocketAsyncEventArgs messageArgs)
        {
            this.socket = socket;
            this.endPoint = socket.RemoteEndPoint;
            this.onDisconnected = onDisconnected;
            this.messageArgs = messageArgs;
            this.messageArgs.Completed += new EventHandler<SocketAsyncEventArgs>(Message_Completed);
            this.messageArgs.UserToken = socket;

            this.StartReceive();
        }

        private void Message_Completed(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessMessage(e);
        }
        ...

 

As I said, if the client be disconnected or send 0-length data, the connected client socket should be closed.

        ...
        private void ProcessMessage(SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //not implemented yet
            }
            else
            {
                this.CloseClientSocket();
            }
        }

        private void StartReceive()
        {

            if (this.socket.Connected)
            {
                if (!socket.ReceiveAsync(this.messageArgs))
                {
                    this.ProcessMessage(this.messageArgs);
                }
            }
        }
        ...

 

I think it is good to fire the onDisconnected action inside this CloseClientSocket method, which is originated from the listener,.

        private void CloseClientSocket()
        {
            if (this.socket != null)
            {
                if (this.socket.Connected)
                {
                    try
                    {
                        this.socket.Shutdown(SocketShutdown.Both);
                    }
                    catch (SocketException socketExcept)
                    {
                        throw new SocketException(socketExcept.ErrorCode);
                    }
                    catch { }
                    finally
                    {
                        this.socket.Close();
                        this.socket = null;
                    }
                }

                this.messageArgs.Completed -= Message_Completed;

                if (onDisconnected != null)
                {
                    onDisconnected(this.endPoint);
                }
            }
        }

 

Implementing the Console Socket Server

It is very easy to use the SocketListener.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press enter key to start server");
            Console.ReadLine();

            SocketListener server = new SocketListener(IPAddress.Parse("127.0.0.1"),4096,10,4096);
            server.OnStarted = new Action<Socket>(OnStarted);
            server.OnStopped = new Action<IPEndPoint>(OnStopped);
            server.OnConnected = new Action<Socket>(OnConnected);
            server.OnDisconnected = new Action<EndPoint>(OnDisconnected);
            server.StartListener();

            Console.WriteLine("Press enter key to stop server");
            Console.ReadLine();

            server.StopListener();
        }

        static void OnStarted(Socket socket)
        {
            LogPrint("Server Started", socket.LocalEndPoint.ToString());
        }

        static void OnStopped(IPEndPoint endPoint)
        {
            LogPrint("Server Stopped", endPoint.ToString());
        }

        static void OnConnected(Socket socket)
        {
            LogPrint("Client Connected", socket.RemoteEndPoint.ToString());
        }

        static void OnDisconnected(EndPoint endPoint)
        {
            LogPrint("Client Disconnected", endPoint.ToString());
        }

        static void LogPrint(string lable, string message)
        {
            Console.WriteLine(string.Format("[{0}] {1}", lable, message));
        }
    }

 

Implemeting the basic SocketClient class

The SocketClient is very similar with the listener class. But this use the Socket.ConnectAsync operation instead of the AcceptAsync operation.

    public class SocketClient
    {
        private Socket socket;
        private SocketAsyncEventArgs connectArgs;

        public Action<Socket> Connected;
        public Action<Socket> Disconnected;

        public SocketClient()
        {
            this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            connectArgs = new SocketAsyncEventArgs();
            connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ConnectCompleted);
            connectArgs.UserToken = this.socket;
        }

        public bool IsConnected()
        {
            bool result = false;

            if (this.socket != null)
            {
                if (this.socket.Connected)
                {
                    result = true;
                }
            }

            return result;
        }

        public void Connect(IPAddress address, int port)
        {
            lock (this)
            {
                this.connectArgs.RemoteEndPoint = new IPEndPoint(address,port);
                if (!this.socket.ConnectAsync(connectArgs))
                {
                    this.ConnectCompleted(this, this.connectArgs);
                }
            }
        }

        public void Disconnect()
        {
            lock (this)
            {
                if (IsConnected())
                {
                    this.socket.Shutdown(SocketShutdown.Both);
                    this.socket.Close();
                    this.socket = null;
                    this.connectArgs = null;
                }

                if (Disconnected != null)
                {
                    Disconnected(this.socket);
                }
            }
        }

        private void ConnectCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                if (Connected != null)
                {
                    Connected(this.socket);
                }
            }
        }
    }

 

Implementing the Console Socket Client

Well, it is also simple to the client making.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press enter key to connect");
            Console.ReadLine();

            SocketClient client = new SocketClient();
            client.Connected = new Action<Socket>(Connected);
            client.Disconnected = new Action<Socket>(Disconnected);
            client.Connect(IPAddress.Parse("127.0.0.1"), 4096);

            Console.WriteLine("Press enter key to disconnect");
            Console.ReadLine();
            client.Disconnect();
        }

        static void Connected(Socket socket)
        {
            Console.WriteLine("Connected");
        }

        static void Disconnected(Socket socket)
        {
            Console.WriteLine("Disconnected");
        }
    }

 

Running the console apps.

Here is the result console apps. Three clients had connected the console server and one client has disconnected.

 

You can download this sample1 source project file at here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: