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.

No comments:

Post a Comment