Sunday 8 June 2014

Sockets Part II

The previous post should give a good idea about how two programs can chat, but the example was really trivial. In this post, I'd like to progress to make it into something more realistic by taking in some real world requirements.

Servers need to listen constantly
The greeting program as it stands waits for a connection, sends a message to any program that joins and then quits. That's not very useful, so to progress toward something that is more useful we need to be able to accept any number of requests. I can do this easily by adding a loop so the server looks like this:

        static void Main()
        {
            var thisMachine = Dns.GetHostAddresses("")[0];
            var server = new Socket(thisMachine.AddressFamily,                   SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(thisMachine, 4510));
            server.Listen(10);

            while (true)
            {
                var client = server.Accept();
                client.Send(Encoding.ASCII.GetBytes("Hello"));
            }
        }

This is how any real server is going to operate, by looping and handling requests from clients until someone kills it.

Chatting is a two way thing
The server is not limited to sending out messages, it's a two way interaction so the client can also send messages. Let's change the server to accept a message from a client and react differently depending on the message.

while (true)
{
    var client = server.Accept();
    var request = new byte[5];
    client.Receive(request);

    switch (Encoding.ASCII.GetString(request))
    {
        case "Hello":
           client.Send(Encoding.ASCII.GetBytes("Hello"));
           break;
        case "Clock":  
           client.Send(Encoding.ASCII.GetBytes(
                      DateTime.Now.ToShortTimeString()));
           break;
    }
}

Must allow parallel conversations
There is a big problem with the above implementation. If I was to create a client that connected and waited a long time before sending a request, or didn't send a request at all, it would block others from connecting. The normal way to get around this is to create a separate thread for each client, but C# also offers tasks so I'm going to use that instead.

I'm going to reduce the loop to this:

            while (true)
            {
                var client = server.Accept();
                Task.Factory.StartNew(() => Chat(client));
            }

and move the chatting to another method to handle each request.

        private static void Chat(Socket client)
        {
            var request = new byte[5];
            client.Receive(request);

            switch (Encoding.ASCII.GetString(request))
            {
                case "Hello":
                   client.Send(Encoding.ASCII.GetBytes("Hello"));
                    break;
                case "Clock":
                   client.Send(Encoding.ASCII.GetBytes(
                       DateTime.Now.ToShortTimeString()));
                   break;
            }
        }

Now each chat runs in its own task and won't block. And that's it, the basic workings of a server.

Sockets Part I

This post gives an overview of how computer programs can chat with each other. I can't claim to have any great knowledge in this area, I'm just interested enough to try to learn by writing about it. So my first warning is that there are probably mistakes. My second is that this will be a basic overview, my assumption is that anyone reading this knows very little of the area.

First off, there are some obvious and important things to point out.

You need to know the location
If I was writing a program and I wanted two separate classes / functions etc. to interact I would give one a reference to another and let it call what ever it wants. It's pretty simple. With two separate programs it's not that simple because there is no way of knowing where in memory the other program is, and in reality it's probably in the memory of another computer running in some other location in the world. That's the first problem, I need to know where the program is so I can chat with it. This seems to basic to point out but it's good to make things clear.

The solution to this is the IP address and port number. I can use the IP address to identify any computer in the world. Once I know, there are thousands of ports on that computer with each one capable of having a program listening on it. So if I want to chat with another program, I need to identify the machine it is on with the IP address and what port number its listening at.

You need to agree how to transport the data
The messages sent between the programs need a transport mechanism, some low level piece of software that moves it around. For the example below I'm using TCP as the transport layer. TCP guarantees me that a message will be received at the cost of performance. Another option is UDP, which does not guarantee delivery but benefits from being quite quick because of this. The general rule I've come across here is use UDP when you want performance and TCP when you want reliability. That's why VOIP tends to use UDP and web browsers tend to use TCP.

You need to be talking the same language
Chatting within a single program gives you all of the advantages of types. One things gives a number to another, you can be guaranteed that the other will receive a number and can do all sorts of numerical things with it. When chatting between two programs, there are no numbers or strings, only streams of bytes. Each byte represents a character encoding from a character set and both programs need to agree on the character set that they will use.

There's lots to learn in this area (see here http://www.joelonsoftware.com/articles/Unicode.html or here http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF) but the important thing is that both programs are using the same character set.

Working through an example
To keep this simple I want to show as little as possible in each step. First is to show that the two programs can chat. I'll create a C# server that will listen for a connection, greet a client that connects by saying 'Hello' and shuts down. Sounds trivial, but there's quite a bit going on.
static void Main()
{
  var thisMachine = Dns.GetHostAddresses("")[0];
  var server = new Socket(thisMachine.AddressFamily,       SocketType.Stream, ProtocolType.Tcp);
    server.Bind(new IPEndPoint(thisMachine, 4510));
  server.Listen(10);

  var client = server.Accept();
  client.Send(Encoding.ASCII.GetBytes("Hello"));
}
And a python client that connects and displays the received message.

        s = socket.create_connection(('', 4510))
        print s.recv(5)
        s.close()

Let's look at what happens in more detail

            var ipAddr = Dns.GetHostAddresses("")[0];

This will get the localhost addresses ("" tells is to use local host). The addresses returned here will be all of the available addresses for this machine, both IPV4 and IPV6, and I'm just picking the first one off the list.

            var server = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

Next step is to create a socket. This bit is important because there are two types of sockets, server sockets and client sockets. The socket we are creating on this line is a server socket and it will be responsible for listening and hooking other programs up with client sockets. This socket is setup to chat over TCP using streams.

            server.Bind(new IPEndPoint(ipAddr, 4510));

Telling the server socket where to listen...

            server.Listen(10);

...and to listen for anyone that wants to chat. The number 10 here is the number of clients that we will allow to back up before going kaput. It's very unlikely that we will reach 10 because the server socket does nothing other than listen and...

            var client = server.Accept();

...accept incoming requests. The call to accept here is blocking, so at this stage the server will wait until another program connects to the end point that it is listening to. This will be done by the python client.

            s = socket.create_connection(('', 4510))

Once our python client connects to the correct location, our server can move on.

                Side Note: It's probably obvious that if you use an incorrect IP address or port number that you won't be able to locate the server. What may not be that obvious is that looking for an IPV4 address when the server is at an IP6 address will mean you're looking at the wrong place. When I get the server local address above I pick the first one off the list so I don't know whether it's an IPV4 or IPV6 address. I get around this by using create_connection() rather than create() from the socket in the python program. create_connection() tells it to look at all available address on the machine.

            client.Send(Encoding.ASCII.GetBytes("Hello"));

The server has created a client to talk to the python client from Accept() and uses that client to send the greeting in bytes, because that's the highest form of communication between two programs. Note that the encoding is ASCII, I'm using ASCII because that's the default in python. So we send the string "Hello", encoded in ASCII, as an array of bytes...

                Side Note: Picking the wrong character set can mess things up. If I switch the line that sends the message to be...

              client.Send(Encoding.Unicode.GetBytes("Hello"));

                ... the resulting string in python would be something like 'H<Null>e<Null>l'. This is because Unicode (this is badly named in C#, it should be UTF-16) uses two bytes per character and ASCII only uses one byte per character.

            print s.recv(5)

...and our python client receives 5 bytes and prints the result. And that's it, two programs have chatted.