Saturday, July 12, 2008

Web MVC Design Pattern and ASP.NET MVC Framework

Even though MVC was designed as a framework for desktop applications three decades ago - it fits quite well in web applications which require URL mapping or/and direct handling of http messages (requests/response); this article reviews the implementation of MVC in web applications of that nature and shows how MVC is used as base architecture in ASP.NET MVC Framework.

MVC for Desktop Applications

MVC design pattern was introduced in the early 80's as a framework for desktop applications; at the time, widgets didn't had the ability to process user inputs (e.g. mouse click, keyboard press) so MVC introduced the controller entity to act as a bridge between the user and the application; in the late 80's widgets came out of the box with the ability to process user inputs so the controller screened-out and MVC twisted into MVP. Even though MVP presenter has been often referred to as controller - the real controller (the one that process user inputs) stayed in the shadows until the introduction of browser based web applications.

MVC for Web Applications

In web applications the user interact with client side browser that summarize his activities as http request (POST or GET) and send it to the server, the server process the request (i.e. pulls data from the URL or from the request message body), performs some business operation and create response stream (HTML page) at which it send back to the client that renders it to the screen.

image

Collaboration

MVC components reside in the server - the model stores data and handles business logic like always, the view generates response stream (HTML page) according to model data, the controller processes user inputs summarized as http requests (POST and GET), commands the model, changes model state, creates the proper view that is downloaded to the client and rendered to the screen.  

image

Interaction

The 'Votes Counter' UI that is shown bellow has one simple use-case. [Trigger:] The user click on the 'Vote' button [Main Success Scenario:] the DB votes field is increased by one and the text-box display is updated with the current votes value.

image

MVC design for this UI will have one view object to generate the HTML shown above, one model object to increase the votes count and to hold the total votes amount, and one controller to process the post request initiated by the 'Vote!' button, command the model to increase vote, create the view to generate updated HTML and return the response to the client.

The diagram bellow shows how MVC objects interact.

image

MVC in ASP.NET

Quick way to get familiar with ASP.NET MVC framework if through this video.

ASP.NET MVC framework enables rapid implementation of MVC based web applications; it was designed specially for applications which require more control over the HTTP input/output than the traditional ASP.NET Web Forms allows. In the Web Forms model, the input goes into the page (view) that's responsible for both handling the input and generating the output, in MVC, the input goes into the controller that's responsible for handling the input, and the page (view) is responsible for generating the output. You can read more about 'Building Web Apps without Web Forms' here.

ASP.NET MVC Framework includes a URL mapping component that enables building applications with clean URLs; The application defines controllers that contains number of predefined actions where each action process specific request, the process sequence includes executing application logic and retrieving data from the domain model up to generating the response through a view component. The framework automatically maps URL's with friendly names ("/controller name/controller action/action parameters") to action in the controller class and invokes the action with the proper parameters.

image

Let's say that the URL '/Home/About/4l' is submitted from the browser, the framework will map the URL to 'About' action of the 'HomeController' class and invoke the action with the parameter '4', the action will do whatever application logic that it requires to do, gets some 'about' data from the model, creates the proper view and post back to the browser. 

Of course,  ASP.NET MVC Framework has a lot more features than I've mentioned, it supplies generic base classes for the views, it automatically bind the views to the domain objects, it supports using the existing ASP.NET .ASPX, .ASCX files and a lot more.

Interesting links

http://osteele.com/archives/2004/08/web-mvc

http://www.hanselman.com/blog/ASPNETMVCWebFormsUnplugged.aspx

Chris Tavares - Building Web Apps without Web Forms

Saturday, July 5, 2008

Sending Typed (Serialized) Messages over .NET Sockets (C# Source Code Included)

Client-server communication via .NET sockets can be established pretty easily by using the 'Sockets communication' package introduced in .NET Socket. The package supports sending/receiving raw array of bytes in two directions and allows multiple clients connection. 

image

In some applications we'll rather transfer structured data in the form of typed messages over raw array of bytes. To accomplish this we need to add another tiny layer on the top of the communication package; the extra layer serialize typed message into bytes array on the sender side and de-serialize bytes array into  typed message in the receiver side.

image

In this post you can find all the code that you need in order to add the referred layer to .NET Socket communication packaged.

Implementation

MessageComposer

MessageComoser is used to convert message (that derive from MessageBase) to bytes[] and to convert bytes to message.

public class MessageComposer
{
    public static byte[] Serialize(int messageKind, MessageBase msg)
    {
        MemoryStream ms = new MemoryStream();
        BinaryFormatter bf1 = new BinaryFormatter();

        bf1.Serialize(ms, messageKind);
        bf1.Serialize(ms, msg);

        return ms.ToArray();
    }

    public static void Deserialize(byte[] buffer, 
        out int messageKind, out MessageBase msg)
    {
        MemoryStream ms = new MemoryStream(buffer);
        BinaryFormatter formatter = new BinaryFormatter();
        messageKind = (int)formatter.Deserialize(ms);
        msg = (MessageBase)formatter.Deserialize(ms);
    }
}

 

Concrete Message

Message should inherit from MessageBase so it can be serialized and deserialized via MesageComposer. The following message carry message string and time. It is used to measure network latency.

[Serializable]
 public class SendingTimeMessage: MessageBase
 {
     private DateTime m_time;
     private string m_message;

     public SendingTimeMessage(DateTime time, string message)
     {
         m_time = time;
         m_message = message;
     }

     public DateTime Time
     {
         get { return m_time; }
     }

     public string Message
     {
         get { return m_message; }
     }

     public TimeSpan CalcSpan()
     {
         return DateTime.Now - m_time;
     }
 }

 

Sending Concrete-Message

MessageComposer is used to covert SendingTimeMessage into bytes, the bytes are than being sent using ClientTerminal.

string mes = "Message content...";

// Create the concrete message
SendingTimeMessage message = new SendingTimeMessage(mes);

int messageKind = (int)MessageKind.SendingTime;

byte[] buffer = MessageComposer.Serialize(messageKind, message);

// Send the message (as bytes) to the server.
m_ClientTerminal.SendMessage(buffer);
Receiving Concrete-Message

MessageComposer is used to covert bytes received from the client into message, the message is being converted to string and presented on the screen. Then, the bytes are being distributed to all connected clients.

void m_Terminal_MessageRecived(Socket socket, byte[] buffer)
{
    string message = ConvertBytesToString(buffer);

    PresentMessage(listMessages, string.Format("Sockets: {0}", message));

    // Send Echo
    m_ServerTerminal.DistributeMessage(buffer);
}


private string ConvertBytesToString(byte[] bytes)
{
    int messageKind;
    MessageBase msg;
    MessageComposer.Deserialize(bytes, out messageKind, out msg);

    MessageKind kind = (MessageKind) messageKind;

    switch(kind)
    {
        case MessageKind.SendingTime:
            SendingTimeMessage sendingTimeMessage = (SendingTimeMessage)msg;
            return "SendingTimeMessage: " + sendingTimeMessage.Message;

        case MessageKind.Simple:
            SimpleMessage simpleMessage = (SimpleMessage)msg;
            return "SimpleMessage: " + simpleMessage.Message;
    }

    return "UnKnown";
}

 

Sample project

Download from here

Tuesday, July 1, 2008

.NET Sockets in Two Directions with Multiple Client Support (C# Source Code Included)

  • This post contains generic code that's ready for use.
  • Full solution is available at the end of the post.

Preface

This post will walk you through the implementation of a simple client-server application that establishes two way communication via .NET sockets while using infrastructure package that extracts the low level sockets API from the application.

image

The basic package can be added with an extra layer which will allow the transport of typed messages, please refer to ‘Sending Typed (Serialized) Messages’ for in detail review and case study.

If you don’t need multiple client support, please refer to ".NET Sockets - Single Client"

Implementation

Server-Side
Server Terminal

ServerTerminal opens TCP port, waits for clients connection, accepts multiple connections, listen to clients messages (bytes array) and allow to broadcast messages (bytes array) to all connected client.

Every client that connect to the server is wrapped-up in ConnectedClient object and added to clients collection. 

public class ServerTerminal
{
    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_ConnectDel ClientConnect;
    public event TCPTerminal_DisconnectDel ClientDisconnect;

    private Socket m_socket;
    private bool m_Closed;

    private Dictionary<long, ConnectedClient> m_clients = 
        new Dictionary<long, ConnectedClient>();
    
    public void StartListen(int port)
    {
        IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);

        m_socket = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp);
        
        try
        {
            m_socket.Bind(ipLocal);
        }
        catch(Exception ex)
        {
            Debug.Fail(ex.ToString(),
                string.Format("Can't connect to port {0}!", port));
            
            return;
        }

        m_socket.Listen(4);
        
        // Assign delegate that will be invoked when client connect.
        m_socket.BeginAccept(new AsyncCallback(OnClientConnection), null);
    }

    private void OnClientConnection(IAsyncResult asyn)
    {
        if (m_Closed)
        {
            return;
        }

        try
        {
            Socket clientSocket = m_socket.EndAccept(asyn);

            RaiseClientConnected(clientSocket);
            
            ConnectedClient connectedClient = new ConnectedClient(clientSocket);

            connectedClient.MessageRecived += OnMessageRecived;
            connectedClient.Disconnected += OnClientDisconnection;

            connectedClient.StartListen();

            long key = clientSocket.Handle.ToInt64();
            if (m_clients.ContainsKey(key))
            {
                Debug.Fail(string.Format(
                    "Client with handle key '{0}' already exist!", key));
            }

            m_clients[key] = connectedClient;
            
            // Assign delegate that will be invoked when next client connect.
            m_socket.BeginAccept(new AsyncCallback(OnClientConnection), null);
        }
        catch (ObjectDisposedException odex)
        {
            Debug.Fail(odex.ToString(),
                "OnClientConnection: Socket has been closed");
        }
        catch (Exception sex)
        {
            Debug.Fail(sex.ToString(), 
                "OnClientConnection: Socket failed");
        }
    }

    private void OnClientDisconnection(Socket socket)
    {
        RaiseClientDisconnected(socket);

        long key = socket.Handle.ToInt64();
        if (m_clients.ContainsKey(key))
        {
            m_clients.Remove(key);
        }
        else
        {
            Debug.Fail(string.Format(
                "Unknown client '{0}' has been disconnected!", key));
        }
    }
  public void DistributeMessage(byte[] buffer)
  {
      try
      {
          foreach (ConnectedClient connectedClient in m_clients.Values)
          {
              connectedClient.Send(buffer);
          }
      }
      catch (SocketException se)
      {
          Debug.Fail(se.ToString(), string.Format(
             "Buffer could not be sent"));
      }
 }
    public void Close()
    {
        try
        {
            if (m_socket != null)
            {
                m_Closed = true;

                // Close the clients
                foreach (ConnectedClient connectedClient in m_clients.Values)
                {
                    connectedClient.Stop();
                }

                m_socket.Close();

                m_socket = null;
            }
        }
        catch (ObjectDisposedException odex)
        {
            Debug.Fail(odex.ToString(), "Stop failed");
        }
    }

    private void OnMessageRecived(Socket socket, byte[] buffer)
    {
        if (MessageRecived != null)
        {
            MessageRecived(socket, buffer);
        }
    }

    private void RaiseClientConnected(Socket socket)
    {
        if (ClientConnect != null)
        {
            ClientConnect(socket);
        }
    }

    private void RaiseClientDisconnected(Socket socket)
    {
        if (ClientDisconnect != null)
        {
            ClientDisconnect(socket);
        }
    }
}
ConnectedClient

This class is instantiated for each client that connect to the server. It utilizes the SocketListener class (will be reviewed shortly) which listen and delegate the messages coming from the client.

public class ConnectedClient
{
    // Hold reference to client socket to allow sending messages to client
    private Socket m_clientSocket;
    SocketListener m_listener;

    public ConnectedClient(Socket clientSocket)
    {
        m_clientSocket = clientSocket;
        m_listener = new SocketListener();
    }

    // Register directly to SocketListener event
    public event TCPTerminal_MessageRecivedDel MessageRecived
    {
        add
        {
            m_listener.MessageRecived += value;
        }
        remove
        {
            m_listener.MessageRecived -= value;
        }
    }

    // Register directly to SocketListener event
    public event TCPTerminal_DisconnectDel Disconnected
    {
        add
        {
            m_listener.Disconnected += value;
        }
        remove
        {
            m_listener.Disconnected -= value;
        }
    }

    public void StartListen()
    {
        m_listener.StartReciving(m_clientSocket);
    }

    public void Send(byte[] buffer)
    {
        if (m_clientSocket == null)
        {
            throw new Exception("Can't send data. ConnectedClient is Closed!");
        }
        m_clientSocket.Send(buffer);
        
    }

    public void Stop()
    {
        m_listener.StopListening();
        m_clientSocket = null;
    }
}
Server Host (Console)

The server host instantiate the ServerTerminal, register to the appropriate events and call StartListening. As a result, multiple clients can connect to its port and start sending/receiving messages.

m_ServerTerminal = new ServerTerminal();

m_ServerTerminal.MessageRecived += m_Terminal_MessageRecived;
m_ServerTerminal.ClientConnect += m_Terminal_ClientConnected;
m_ServerTerminal.ClientDisconnect += m_Terminal_ClientDisConnected;

m_ServerTerminal.StartListen(alPort);
Both-Sides
Socket Listener

SocketListener allows both ServerTerminal and ClientTetminal to listen to messages coming a socket. When a message arrives – the SocketListener figures out whether it represents new data or whether it represents 'connection dropped' message. In case the message represents new data it raises the MessageReceived event and waits for the next message. In case the message indicate that the connection has been dropped - it raises the Disconnected event and exits.

public class SocketListener
{
    private const int BufferLength = 1000;
    AsyncCallback pfnWorkerCallBack;
    Socket m_socWorker;

    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_DisconnectDel Disconnected;

    public void StartReciving(Socket socket)
    {
        m_socWorker = socket;
        WaitForData(socket);
    }

    private void WaitForData(System.Net.Sockets.Socket soc)
    {
        try
        {
            if (pfnWorkerCallBack == null)
            {
                pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
            }
            
            CSocketPacket theSocPkt = new CSocketPacket(BufferLength);
            theSocPkt.thisSocket = soc;

         // Start waiting asynchronously for single data packet
         soc.BeginReceive(
                theSocPkt.dataBuffer,
                0,
                theSocPkt.dataBuffer.Length,
                SocketFlags.None,
                pfnWorkerCallBack,
                theSocPkt);
        }
        catch (SocketException sex)
        {
            Debug.Fail(sex.ToString(), "WaitForData: Socket failed");
        }

    }

    private void OnDataReceived(IAsyncResult asyn)
    {
        CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState;
        Socket socket = theSockId.thisSocket;

        if (!socket.Connected)
        {
            return;
        }

        try
        {
            int iRx;
            try
            {
                iRx = socket.EndReceive(asyn);
            }
            catch (SocketException)
            {
                Debug.Write("Client has been closed and cannot answer.");

                OnConnectionDroped(socket);
                return;
            }

            if (iRx == 0)
            {
                Debug.Write("Client socket has been closed.");

                OnConnectionDroped(socket);
                return;
            }

            RaiseMessageRecived(theSockId.dataBuffer);
       // Wait for the next package
            WaitForData(m_socWorker);
        }
        catch (Exception ex)
        {
            Debug.Fail(ex.ToString(), "OnClientConnection: Socket failed");
        }
    }

    public void StopListening()
    {
        if (m_socWorker != null)
        {
            m_socWorker.Close();
            m_socWorker = null;
        }
    }

    private void RaiseMessageRecived(byte[] buffer)
    {
        if (MessageRecived != null)
        {
            MessageRecived(m_socWorker, buffer);
        }
    }

    private void OnDisconnection(Socket socket)
    {
        if (Disconnected != null)
        {
            Disconnected(socket);
        }
    }

    private void OnConnectionDroped(Socket socket)
    {
        m_socWorker = null;
        OnDisconnection(socket);
    }
}

public class CSocketPacket
{
    public System.Net.Sockets.Socket thisSocket;
    public byte[] dataBuffer;

    public CSocketPacket(int buffeLength)
    {
        dataBuffer = new byte[buffeLength];
    }
}
Client-Side
Client Terminal

ClientTerminal connects to TCP port, sends messages (bytes array) to the server and listens to server messages (bytes array).

public class ClientTerminal
{
    Socket m_socClient;
    private SocketListener m_listener;

    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_ConnectDel Connected;
    public event TCPTerminal_DisconnectDel Disconncted;

    public void Connect(IPAddress remoteIPAddress, int alPort)
    {
        m_socClient = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp);
        
        IPEndPoint remoteEndPoint = new IPEndPoint(remoteIPAddress, alPort);
        
        m_socClient.Connect(remoteEndPoint);

        OnServerConnection();
    }

    public void SendMessage(byte[] buffer)
    {
        if (m_socClient == null)
        {
            return;
        }
        m_socClient.Send(buffer);

    }

    public void StartListen()
    {
        if (m_socClient == null)
        {
            return;
        }

        if (m_listener != null)
        {
            return;
        }

        m_listener = new SocketListener();
        m_listener.Disconnected += OnServerConnectionDroped;
        m_listener.MessageRecived += OnMessageRecvied;
        
        m_listener.StartReciving(m_socClient);
    }

    public void Close()
    {
        if (m_socClient == null)
        {
            return;
        }

        if (m_listener != null)
        {
            m_listener.StopListening();
        }

        m_socClient.Close();
        m_listener = null;
        m_socClient = null;
    }

    private void OnServerConnection()
    {
        if (Connected != null)
        {
            Connected(m_socClient);
        }
    }

    private void OnMessageRecvied(Socket socket, byte[] buffer)
    {
        if (MessageRecived != null)
        {
            MessageRecived(socket, buffer);
        }
    }

    private void OnServerConnectionDroped(Socket socket)
    {
        Close();
        RaiseServerDisconnected(socket);
    }

    private void RaiseServerDisconnected(Socket socket)
    {
        if (Disconncted != null)
        {
            Disconncted(socket);
        }
    }
}
Client Host (Console)

Client host should instantiate ClientTerminal and call 'Connect' with server-name/IP-address and port. After that call - m_terminal can be used to send/receive messages to/from the server.

m_ClientTerminal = new ClientTerminal();

m_ClientTerminal.Connected += m_TerminalClient_Connected;
m_ClientTerminal.Disconncted += m_TerminalClient_ConnectionDroped;
m_ClientTerminal.MessageRecived += m_TerminalClient_MessageRecived;

m_ClientTerminal.Connect(remoteIPAddress, alPort);

 

Sample project

Download from here

.NET Sockets - Two Way - Single Client (C# Source Code Included)

  • This post contains generic code that's ready for use.
  • The code was tested and debugged.
  • Full solution is available at the end of the post.

Preface

This post will walk you through the implementation of 'client-server' communication package with which single clients can establish communication with the server, send bytes array, and listen to server messages.

This package can be added with extra layer in order to support typed messages transport - code + details can be found here.

If you need multiple clients support -  code + details can be found here.

Single Client - Socket based Communication

This post reviews the most simple implementation of client-server socket based application. It shows how single client can establish communication with listening server and send/recive bytes array to/from the server.

Implementation

Server-Side
Server Terminal

ServerTerminal opens TCP port, accepts connection from single client, listens to client messages (bytes array) and sends messages (bytes array) to the connected client.

It utilizes the SocketListener class (that will be latter described) to allow incoming messages from connected clients.

public class ServerTerminal
{
    Socket m_socket;
    SocketListener m_listener;
    
    private bool m_Closed;
    private Socket m_socWorker;

    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_ConnectDel ClientConnect;
    public event TCPTerminal_DisconnectDel ClientDisconnect;
    
    public void StartListen(int port)
    {
        IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);

        m_socket = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp);
        
        try
        {
            m_socket.Bind(ipLocal);
        }
        catch(Exception ex)
        {
            Debug.Fail(ex.ToString(),
                string.Format("Can't connect to port {0}!", port));
            
            return;
        }

        m_socket.Listen(4);

        // Assign delegate that will be invoked when client connect.
        m_socket.BeginAccept(new AsyncCallback(OnClientConnection), null);
    }

    private void OnClientConnection(IAsyncResult asyn)
    {
        if (m_Closed)
        {
            return;
        }

        try
        {
            m_socWorker = m_socket.EndAccept(asyn);

            RaiseClientConnected(m_socWorker);
            
            m_listener = new SocketListener();
            m_listener.MessageRecived += OnMessageRecived;
            m_listener.Disconnected += OnClientDisconnection;

            m_listener.StartReciving(m_socWorker);
        }
        catch (ObjectDisposedException odex)
        {
            Debug.Fail(odex.ToString(), 
                "OnClientConnection: Socket has been closed");
        }
        catch (Exception sex)
        {
            Debug.Fail(sex.ToString(), 
                "OnClientConnection: Socket failed");
        }

    }

    private void OnClientDisconnection(Socket socket)
    {
        RaiseClientDisconnected(socket);

        // Try to re-establish connection
        m_socket.BeginAccept(new AsyncCallback(OnClientConnection), null);

    }
  public void SendMessage(byte[] buffer)
    {
      if (m_socWorker == null)
      {
          return;
      }
      m_socClient.Send(buffer);

  }
    public void Close()
    {
        try
        {
            if (m_socket != null)
            {
                m_Closed = true;

                if (m_listener != null)
                {
                    m_listener.StopListening();
                }

                m_socket.Close();

                m_listener = null;
                m_socWorker = null;
                m_socket = null;
            }
        }
        catch (ObjectDisposedException odex)
        {
            Debug.Fail(odex.ToString(), "Stop failed");
        }
    }

    private void OnMessageRecived(string message)
    {
        if (MessageRecived != null)
        {
            MessageRecived(message);
        }
    }

    private void RaiseClientConnected(Socket socket)
    {
        if (ClientConnect != null)
        {
            ClientConnect(socket);
        }
    }

    private void RaiseClientDisconnected(Socket socket)
    {
        if (ClientDisconnect != null)
        {
            ClientDisconnect(socket);
        }
    }
}
Server Host (Console)

Server host should instantiate ServerTerminal and call StartListening. After that call - Single client can connect to its port and start sending/receiving messages.

m_ServerTerminal = new ServerTerminal();

m_ServerTerminal.MessageRecived += m_Terminal_MessageRecived;
m_ServerTerminal.ClientConnect += m_Terminal_ClientConnected;
m_ServerTerminal.ClientDisconnect += m_Terminal_ClientDisConnected;

m_ServerTerminal.StartListen(alPort);

 

Both-Sides
Socket Listener

SocketListener allows both ServerTerminal and ClientTetminal to listen to incoming messages from the other side.

public class SocketListener
{
    private const int BufferLength = 1000;
    AsyncCallback pfnWorkerCallBack;
    Socket m_socWorker;

    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_DisconnectDel Disconnected;

    public void StartReciving(Socket socket)
    {
        m_socWorker = socket;
        WaitForData(socket);
    }

    private void WaitForData(System.Net.Sockets.Socket soc)
    {
        try
        {
            if (pfnWorkerCallBack == null)
            {
                pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
            }
            
            CSocketPacket theSocPkt = new CSocketPacket(BufferLength);
            theSocPkt.thisSocket = soc;

            soc.BeginReceive(
                theSocPkt.dataBuffer,
                0,
                theSocPkt.dataBuffer.Length,
                SocketFlags.None,
                pfnWorkerCallBack,
                theSocPkt);
        }
        catch (SocketException sex)
        {
            Debug.Fail(sex.ToString(), "WaitForData: Socket failed");
        }

    }

    private void OnDataReceived(IAsyncResult asyn)
    {
        CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState;
        Socket socket = theSockId.thisSocket;

        if (!socket.Connected)
        {
            return;
        }

        try
        {
            int iRx;
            try
            {
                iRx = socket.EndReceive(asyn);
            }
            catch (SocketException)
            {
                Debug.Write("Apperently client has been closed and connot answer.");

                OnConnectionDroped(socket);
                return;
            }

            if (iRx == 0)
            {
                Debug.Write("Apperently client socket has been closed.");

                OnConnectionDroped(socket);
                return;
            }

            RaiseMessageRecived(theSockId.dataBuffer);

            WaitForData(m_socWorker);
        }
        catch (Exception ex)
        {
            Debug.Fail(ex.ToString(), "OnClientConnection: Socket failed");
        }
    }

    public void StopListening()
    {
        if (m_socWorker != null)
        {
            m_socWorker.Close();
            m_socWorker = null;
        }
    }

    private void RaiseMessageRecived(byte[] buffer)
    {
        if (MessageRecived != null)
        {
            MessageRecived(m_socWorker, buffer);
        }
    }

    private void OnDisconnection(Socket socket)
    {
        if (Disconnected != null)
        {
            Disconnected(socket);
        }
    }

    private void OnConnectionDroped(Socket socket)
    {
        m_socWorker = null;
        OnDisconnection(socket);
    }
}

public class CSocketPacket
{
    public System.Net.Sockets.Socket thisSocket;
    public byte[] dataBuffer;

    public CSocketPacket(int buffeLength)
    {
        dataBuffer = new byte[buffeLength];
    }
}
Client-Side
Client Terminal

ClientTerminal connects to TCP port, sends messages (bytes array) to the server and listens to server messages (bytes array).

public class ClientTerminal
{
    Socket m_socClient;
    private SocketListener m_listener;

    public event TCPTerminal_MessageRecivedDel MessageRecived;
    public event TCPTerminal_ConnectDel Connected;
    public event TCPTerminal_DisconnectDel Disconncted;

    public void Connect(IPAddress remoteIPAddress, int alPort)
    {
        m_socClient = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp);
        
        IPEndPoint remoteEndPoint = new IPEndPoint(remoteIPAddress, alPort);
        
        m_socClient.Connect(remoteEndPoint);

        OnServerConnection();
    }

    public void SendMessage(byte[] buffer)
    {
        if (m_socClient == null)
        {
            return;
        }
        m_socClient.Send(buffer);

    }

    public void StartListen()
    {
        if (m_socClient == null)
        {
            return;
        }

        if (m_listener != null)
        {
            return;
        }

        m_listener = new SocketListener();
        m_listener.Disconnected += OnServerConnectionDroped;
        m_listener.MessageRecived += OnMessageRecvied;
        
        m_listener.StartReciving(m_socClient);
    }

    public void Close()
    {
        if (m_socClient == null)
        {
            return;
        }

        if (m_listener != null)
        {
            m_listener.StopListening();
        }

        m_socClient.Close();
        m_listener = null;
        m_socClient = null;
    }

    private void OnServerConnection()
    {
        if (Connected != null)
        {
            Connected(m_socClient);
        }
    }

    private void OnMessageRecvied(Socket socket, byte[] buffer)
    {
        if (MessageRecived != null)
        {
            MessageRecived(socket, buffer);
        }
    }

    private void OnServerConnectionDroped(Socket socket)
    {
        Close();
        RaiseServerDisconnected(socket);
    }

    private void RaiseServerDisconnected(Socket socket)
    {
        if (Disconncted != null)
        {
            Disconncted(socket);
        }
    }
}
Client Host (Console)

Client host should instantiate ClientTerminal and call 'Connect' with server-name/IP-address and port. After that call - m_terminal can be used to send/receive messages to/from the server.

m_ClientTerminal = new ClientTerminal();

m_ClientTerminal.Connected += m_TerminalClient_Connected;
m_ClientTerminal.Disconncted += m_TerminalClient_ConnectionDroped;
m_ClientTerminal.MessageRecived += m_TerminalClient_MessageRecived;

m_ClientTerminal.Connect(remoteIPAddress, alPort);

 

Sample project

Download from here