dinsdag 20 december 2016

TCP Server in C# (part 2)

How to write a TCP Server in C# - part 2

This is part two of a serie about implementing a decent tcp server through sockets. Part one can be found here.

Part one contains an overview of a TCP Server in C# based on the socket class. Now it's time to show the implementation.

Implementing the listener


using System;
using System.Net;
using System.Net.Sockets;

namespace Yxorp
{
   public class ListenerSocket
   {
      private readonly Socket _wrappedSocket;

      public ListenerSocket(IPAddress ipAddress, int port)
      {
         _wrappedSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         var ipEndPoint = new IPEndPoint(ipAddress, port);

         _wrappedSocket.Bind(ipEndPoint);
         _wrappedSocket.Listen(int.MaxValue);
         _wrappedSocket.BeginAccept(AcceptCallback, _wrappedSocket);
      }

      private static void AcceptCallback(IAsyncResult asyncResult)
      {
         var listenerSocket = (Socket) asyncResult.AsyncState;

         Socket connectionSocket;

         try
         {
            connectionSocket = listenerSocket.EndAccept(asyncResult);
         }
         catch (ObjectDisposedException e) // This exception occurs when the listening socket is closed.
         {
            return;
         }

         var httpSocket = new HttpSocket(connectionSocket);

         httpSocket.BeginRead();

         listenerSocket.BeginAccept(AcceptCallback, listenerSocket);
      }

      public void Close()
      {
         _wrappedSocket.Close();
      }
   }
}

Please not that the implementation of HttpSocket will be shown later in this blog.


Hosting the listener


So how are we going to host this listener in a Windows Service or Console application? As I've shown earlier it's really easy to create a Windows Service that can be run as a console application as well, by using TopShelf.

While the listener can be run by TopShelf directly, it might be a good idea to create a seperate service class that will be started by TopShelf. And then let that service class start the listener. This can be usefull if we need to close more than only the listener, for example also the worker sockets.

So let's create a Service class that can be run with TopShelf:


using System.Net;
using System.Threading;

namespace Yxorp
{
   public class MyService
   {
      private readonly IPAddress _ipAddress;
      private readonly int _port;
      private static ListenerSocket _listenerSocket;

      public MyService(IPAddress ipAddress, int port)
      {
         _ipAddress = ipAddress;
         _port = port;
      }

      public void Start()
      {
         _listenerSocket = new ListenerSocket(_ipAddress, _port);
      }

      public void Stop()
      {
         _listenerSocket.Close();
      }
   }
}



Start the service with TopShelf


Now we can start the service class with TopShelf, by creating a console application:

using System;
using System.Configuration;
using Topshelf;

namespace Yxorp
{
   public class Program
   {
      static void Main()
      {
         var ipAddress = IPAddress.Parse("127.0.0.1");
         var port = 8099;
            
         HostFactory.Run(hostConfigurator =>
         {
            hostConfigurator.Service<MyService>(serviceConfigurator =>
            {
               serviceConfigurator.ConstructUsing(() => new MyService(ipAddress, port));

               serviceConfigurator.WhenStarted(myService => myService.Start());
               serviceConfigurator.WhenStopped(myService => myService.Stop());
            });

            hostConfigurator.RunAsLocalSystem();
            hostConfigurator.SetDescription("Reverse proxy using Topshelf");
            hostConfigurator.SetDisplayName("Yxorp.Net");
            hostConfigurator.SetServiceName("Yxorp.Net");
         });
      }
   }
}



Implementing the worker sockets


We already implemented the socket that is listening for new connections. But that socket is not the one reading the bytes that the client sends. So we need to implement the code for the socket that reads from and writes to the client.


using System;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;

namespace Yxorp
{
   public class HttpSocket
   {
      private readonly Stream _generalStream;
      private readonly Socket _wrappedSocket;
      const string ServerHttpVersion = "HTTP/1.1";
      const string ServerHttpStatuscode = "200";
      private static readonly byte[] CrlfBytes = Encoding.ASCII.GetBytes("\r\n");
      private static readonly byte[] EndBytes = Encoding.ASCII.GetBytes("\r\n\r\n");
      private static readonly string ServerResponse = $"{ServerHttpVersion} {ServerHttpStatuscode}\r\nContent-Length: 0\r\n\r\n";
      private static readonly byte[] ServerResponseBytes = Encoding.UTF8.GetBytes(ServerResponse);
      private static readonly int ServerResponseBytesLength = ServerResponseBytes.Length;


      public HttpSocket(Socket socket)
      {
         _wrappedSocket = socket;
         int bufferSize = socket.ReceiveBufferSize;
         Buffer = new byte[bufferSize];
         AllData = new byte[0];
         RequestLine = new byte[0];

         _generalStream = new NetworkStream(socket);
      }

      public void Close()
      {
         _wrappedSocket.Disconnect(true);
         _generalStream.Close();
         _generalStream.Dispose();
      }

      public void EndWrite(IAsyncResult asyncResult)
      {
         _generalStream.EndWrite(asyncResult);
      }

      public void BeginRead()
      {
         _generalStream.BeginRead(Buffer, 0, Buffer.Length, ReadCallback, this);
      }

      public IntPtr Handle => _wrappedSocket.Handle;

      public byte[] Buffer { get; set; }

      /// <summary>
      /// This is a field and not a property, because Array.Resize can only handle fields.
      /// </summary>
      public byte[] AllData;

      public byte[] RequestLine { get; set; }

      private void BeginWrite()
      {
         _generalStream.BeginWrite(ServerResponseBytes, 0, ServerResponseBytesLength, WriteCallback, this);
      }

      private static void ReadCallback(IAsyncResult asyncResult)
      {
         HttpSocket httpSocket;

         try
         {
            httpSocket = (HttpSocket) asyncResult.AsyncState;
         }
         catch (Exception ex)
         {
            return;
         }

         int numberOfBytesRead;
         try
         {
            numberOfBytesRead = httpSocket.EndRead(asyncResult);
         }
         catch (Exception ex)
         {
            return;
         }

         // Reading Zero Bytes
         // Many stream-oriented objects (including sockets) will signal the end of the stream by
         //    returning 0 bytes in response to a Read operation.
         //    This means that the remote side of the connection has gracefully closed the connection,
         //    and the socket should be closed.
         // The zero-length read must be treated as a special case; if it is not,
         //    the receiving code usually enters an infinite loop attempting to read more data.
         //    A zero-length read is not an error condition; 
         //    it merely means that the socket has been disconnected.
         // source: http://blog.stephencleary.com/2009/06/using-socket-as-connected-socket.html
         if (numberOfBytesRead == 0)
         {
            httpSocket.Close();
            return;
         }

         bool endBytesAreSent = true;
         if (numberOfBytesRead >= 4)
         {
            for (int i = 0; i < 4; i++)
            {
               if (httpSocket.Buffer[numberOfBytesRead - 4 + i] == EndBytes[i])
                  continue;

               endBytesAreSent = false;
               break;
            }
         }
         else
         {
            endBytesAreSent = false;
         }

         httpSocket.Buffer.AddToEndOf(ref httpSocket.AllData, numberOfBytesRead);

         if (endBytesAreSent)
         {
            var request = Encoding.UTF8.GetString(httpSocket.AllData, 0, httpSocket.AllData.Length);

            httpSocket.BeginWrite();
         }
         else
         {
            httpSocket.BeginRead();
         }
      }

      private static void WriteCallback(IAsyncResult asyncResult)
      {
         var socketWrapper = (HttpSocket) asyncResult.AsyncState; 
         socketWrapper.EndWrite(asyncResult);

         socketWrapper.AllData = new byte[0];
         socketWrapper.RequestLine = new byte[0];
         socketWrapper.Buffer = new byte[1024];

         socketWrapper.BeginRead();
      }

      private int EndRead(IAsyncResult asyncResult)
      {
         return _generalStream.EndRead(asyncResult);
      }
   }
}

This workersocket reads all bytes the clients sends, and then returns an empty http response with status code 200.


How to test

If you run the code, and point your browser to http://127.0.0.1:8099 you can see the browser loading for a short while, and then see an empty screen. If you check with the developer tools of your browser, you can see that it makes a call to the TCP Server that we created, and that it get's a http status code 200.

zondag 18 december 2016

Yxorp.Net available at Nuget.org

I just released Yxorp.Net as a Nuget package. See here: https://www.nuget.org/packages/Yxorp.Net

The source is also available at: https://github.com/EdoVanAsseldonk/yxorp.net

More details and info about writing a high performant reverse proxy in C# can be found here: https://edo-van-asseldonk.blogspot.nl/2016/12/tcp-server-in-c.html

zondag 4 december 2016

TCP Server in C#

How to write a TCP Server in C#

What is the most performant way to implement a TCP server in C#? While trying to find an example of a http server in C# I came across this example:
https://msdn.microsoft.com/en-us/library/fx6588te(v=vs.110).aspx

Although is looks pretty good, it lacks a few details:

It can't be stopped in a decent way
It can't be run as a windows service
It doesn't show which exceptions to catch

This is part one of a serie about implementing a decent tcp server through sockets.

Part two can be found here.



Parts of a TCP Server

A TCP server based on sockets in C# is actually pretty simple. It consists of two parts:

A listener, this is a socket that listens for client connections
A handler, this is a socket that handles the communication with the client from the moment the connection is made.

For both parts the Socket class is used. The socket class examples however show the synchronous way of sending and receiving data. While that is fine for an example, it's not the preferred way of writing a scalable, high performant TCP Server. In that case, the asynchronous methods beginXXX must be used. So how does asynchronous communication with sockets work?


How does it work?

Start the listener

First let's start a socket that listens for new connections.



In the BeginAccept method a callback method has to be set. This callback method will be called whenever a new client starts a new connection with the ser.


A client connects to the server

When a client connects to the server, the callback will be triggered.



In the callback a new socket will be made which handles all communication with the client, so the Listener socket will be free for accepting new connections.


Server starts receiving data


The clientsocket calls BeginReceive to asynchronously receive the data.




Restart listening for new connections

On the listenersocket BeginAccept has to be called again, to restart listening for new connections.



This allows new clients to connect. For each new connection, a ClientSocket will be created.






This concludes the overview. In part two the TCP Server will be implemented.