Friday, April 29, 2011

Networking client / server example

At work I have been writing a lot of code relating to sending data over a TCP connection.

I have also seen a couple of questions, recently, on Stack Overflow asking about why networking code wasn't working. Unfortunately I didn't have time to answer them, but it did make me think that there must be a dearth of good samples of networking code online.

Allow me to make that dearth one sample fewer! (Does that make sense?)

For the full listing visit my Github repository: https://github.com/Mellen/Networking-Samples

One problem, that sparked my interest, was how to to keep the server running when a client disconnects. Because the server needs to know when a client disconnects, and not just choke and die. A client disconnecting is not an exceptional circumstance.

The first problem is to not let the server die when a client disconnects, the second is to keep the server looking for new connections, so that it can be a server.

Keep it alive!

My solution to the disconnection problem got generalised to both the client and the server classes, because it makes sense to not have the client die if the server disappears. The user might want to try to reconnect.

You'll find this code in the file NetworkSampleLibrary/NetworkStreamHandler.cs

protected void ReadFromStream(object worker, DoWorkEventArgs args)
{
    BackgroundWorker streamWorker = worker as BackgroundWorker;
    NetworkStream stream = args.Argument as NetworkStream;
    try
    {
        HandleStreamInput(stream);
    }
    catch (Exception ex)
    {
        if (ex is IOException || ex is ObjectDisposedException || ex is InvalidOperationException)
        {
            streamWorker.CancelAsync();
        }

        if (ex is IOException || ex is InvalidOperationException)
        {
            stream.Dispose();
        }

        if (StreamError != null)
        {
            StreamError(ex, stream);
        }
    }
}

You might have noticed that the method is an event handler. More on that below.

As you can see, there are three types of exception that can happen if a client disconnects from the server: IOException, ObjectDisposedException and InvalidOperationException. I found this out through trial and error.

The most common exception that gets thrown when a client disconnects is IOException. This is because the server will be trying to read from the client when it leaves.

Because of the threaded nature of the system, ObjectDisposedExceptions gets thrown when another exception gets thrown and the server still tries to read from the stream in the mean time.

I'm not entirely sure why InvalidOperationException gets thrown, and it doesn't happen a lot, but it is always when the client disconnects.

My strategy is to catch all exceptions, deal with the disconnection exceptions by disposing of the stream if necessary and cancelling the process that reads from the stream, then raising an event that contains the exception and the stream that threw it. I could create a custom exception here, but I settled on an event just in case something that wouldn't catch an exception wanted to know about it.

All are welcome

The next part of the puzzle is to make sure that more than one client can connect to your server.

This is achieved in the NetworkServer class. This can be found at NetworkServerSample / NetworkServer.cs

The pertinent parts are listed below:

public NetworkServer(int port)
{
    _listener = new TcpListener(IPAddress.Any, port);
    _listener.Start();
    _listener.BeginAcceptTcpClient(AcceptAClient, _listener);
    DataAvilable += SendDataToAll;

    StreamError += (ex, stream) =>
        {
            if (ex is IOException || ex is InvalidOperationException || ex is ObjectDisposedException)
            {
                _streams.Remove(stream);
                Console.WriteLine("lost connection {0}", ex.GetType().Name);
            }
            else
            {
                throw ex;
            }
        };
}

private void AcceptAClient(IAsyncResult asyncResult)
{
    TcpListener listener = asyncResult.AsyncState as TcpListener;

    try
    {
        TcpClient client = listener.EndAcceptTcpClient(asyncResult);

        Console.WriteLine("Got a connection from {0}.", client.Client.RemoteEndPoint);

        HandleNewStream(client.GetStream());
    }
    catch (ObjectDisposedException)
    {
        Console.WriteLine("Server has shutdown.");
    }

    if (!_disposed)
    {
        listener.BeginAcceptTcpClient(AcceptAClient, listener);
    }
}

private void HandleNewStream(NetworkStream networkStream)
{
    _streams.Add(networkStream);
    BackgroundWorker streamWorker = new BackgroundWorker();
    streamWorker.WorkerSupportsCancellation = true;
    streamWorker.DoWork += ReadFromStream;
    streamWorker.RunWorkerCompleted += (s, a) =>
                                        {
                                            if (_streams.Contains(networkStream) && !a.Cancelled)
                                            {
                                                streamWorker.RunWorkerAsync(networkStream);
                                            }
                                        };
    streamWorker.RunWorkerAsync(networkStream);
}

In the constructor, the server is set up to listen on a particular port for incoming connections and handle the connection requests asynchronously. It also creates an event handler for when the network stream throws an exception, as explained above. This makes sure that the stream is removed from the list of streams, so that it doesn't try to get disposed of when the server is disposed, and that no data gets broadcast down it.

The method that deals with the asynchronous requests for connection (AcceptAClient) has to make sure that the server hasn't been disposed of when the connection attempt is made, hence the try-catch block. Once the connection request has been handled then the method starts listening for another connection attempt. This is all it takes, essentially asynchronous recursion.

The HandleNewStream method also uses asynchronous recursion to read each message from the client. It sets up a BackgroundWorker instance that asynchronously calls the ReadFromStream method in the previous section, and when the work is complete, the worker will call the method again, so long as the stream is in the list of streams on the server and the worker has not been cancelled.

That's the meat of the server. Accepting and handling input from more than one client is achieved with a list and asynchronous recursion. Dealing with clients disconnecting is done with exception handling and events.

Thursday, April 28, 2011

Really basic programming maths (part 1)

So I've been trying to mentally do hexadecimal addition. I've found that I'm not very good at it.

I'm going to slowly explain how I go about working stuff out, with the hope that it will stick in my head and get easier. (Binary is written with the most significant bit first, and all numbers are unsigned.)

First of all there is how to think about numbers in binary and hex.

Decimal numbers get split up into multiples of powers of ten.

For example 4181 can be broken down as:
  • 4 x 103
  • 1 x 102
  • 8 x 101
  • 1 x 100

Remembering that all numbers raised to 0 are 1.

This applies to both binary and hexadecimal too.

So 0xFEED breaks down to:
  • F(15) x 10(16)3
  • E(14) x 10(16)2
  • E(14) x 10(16)1
  • D(13) x 10(16)0

The numbers in parenthesis are the decimal representations of the hexadecimal numbers.

And 0b1101 breaks down to:
  • 1(1) x 10(2)3
  • 1(1) x 10(2)2
  • 0(0) x 10(2)1
  • 1(1) x 10(2)0

The numbers in parenthesis are the decimal representations of the binary numbers.

Next up is the easy way to transition from hex to binary and back.

Since an individual hex digit takes up to a maximum of four bits, all hex numbers can be represented as collections of four bit numbers.

So 0x4432 can be broken down into 0b0100, 0b0100, 0b0011, 0b0010

This can be reversed. Say you have the 32bit number 0b10011100110100110101101011110011.

If you break it down into four bit chunks you get:
  • 0b1001
  • 0b1100
  • 0b1101
  • 0b0011
  • 0b0101
  • 0b1010
  • 0b1111
  • 0b0011

Each chunk can be represented as a hex digit:
  • 0x9
  • 0xC
  • 0xD
  • 0x3
  • 0x5
  • 0xA
  • 0xF
  • 0x3

Which gives us the number 0x9CD35AF3.

The difficult part comes in getting that number as decimal.

To do it from hex, you need to add up all the powers of sixteen that there are:
  • 9 x 167
  • 12 x 166
  • 13 x 165
  • 3 x 164
  • 5 x 163
  • 10 x 162
  • 15 x 161
  • 3 x 160

Which turns out to be: 2631097075. Not easy to calculate in your head. To do it from binary would take even longer as you would need to add up all the powers of two from 31 to 0.

Thus endeth part one.