18 Comments
Advert: IRC7 is theIRC-client for Windows Phone 7. Learn more at www.softwaremk.org/irc.

This is the part II of a tutorial series which will describe the WP7’s sockets-support from a developer’s perspective. This tutorial series will focus on developing WP7 applications which require a long running TCP-connection that send and receive text-based data. In these posts we will go through of building a complete IRC-client.

The focus of these posts is in the WP7’s developer’s perspective. If you want to better understand the inner details of the Internet sockets, Wikipedia has a good introduction on the subject.

This second installment will continue from where the first part stopped: We have the TCP-connection open and now we must start listening to the messages that the server sends us.

Basics

Like we previously went through, the Socket and the SockectAsyncEventArgs are the two key classes when dealing with sockets in Windows Phone platform. They are used when a connection is created and they are used again when we want to receive messages from the server. We are going to continue with the pattern we established in the first tutorial: An instance of the SockectAsyncEventArgs is always used just once and then disposed.

To receive a message we need a buffer. The buffer is simply an array of byte which is created and attached to the SocketAsyncEventArgs-instance. When the server sends a message to our client, it may or may not fit into a single buffer. If it doesn’t fit, the rest of the message (or the next part) can be received by again creating a new SocketAsyncEventArgs-instance and a buffer for it.

Creating a new buffer for every message is not the only available solution. One could use a Circular buffer to optimize things performance wise. You can check out the “Circular Buffer for .NET”, a project from CodePlex, to see an example implementation.

The code

Like previously mentioned, in addition to the byte array we need an instance of the SocketAsyncEventArgs to receive a message from the server. This is passed to the ReceiveAsync-method of our socket-instance.

        private void ReceiveMessage()
        {
            var responseListener = new SocketAsyncEventArgs();
            responseListener.Completed += OnMessageReceivedFromServer;
            
            var responseBuffer = new byte[bufferSize];
            responseListener.SetBuffer(responseBuffer, 0, bufferSize);

            connection.ReceiveAsync(responseListener);
        }

The new ReceiveMessage-method can be called after the connection to the server has been opened. Now, when the server sends us a message, our method OnMessageReceivedFromServer is executed. In this method we need to do few things:

  1. Convert the buffer (the byte-array) into a string. This is the message (or the messages) that the server sent us.
  2. Parse the message and react to it according to the IRC-specification.
  3. Call the ReceiveMessage-method which will create a new buffer and a new SocketAsyncEventArgs and starts listening for the next message from the server.

One buffer can contain multiple messages from the server.Server can send multiple messages to us, meaning that the even though the OnMessageReceivedFromServer is executed only once, there may be multiple messages we have to deal with. In the IRC-protocol every message ends with a newline and we can use this to our advantage: After turning the byte-array into a string, we split it by the newline characters into a string-array. Every instance in the array is one individual message from the server.

But how can we be sure that the server’s message fit into our buffer? If the last character in the buffer is a newline, all is well. But if it isn’t, our buffer is full and there’s a new message waiting for us. And we have to do some stitching: We can’t process the current message because it’s not complete so we store it into a variable, wait for the next message and combine those two. Using the previously mentioned circular buffer would make this somewhat easier.

        private string trailingMessage;
        private void OnMessageReceivedFromServer(object sender, SocketAsyncEventArgs e)
        {
            // Convert the received message into a string
            var message = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);

            var bufferWasPreviouslyFull = !string.IsNullOrWhiteSpace(trailingMessage);
            if (bufferWasPreviouslyFull)
            {
                message = trailingMessage + message;
                trailingMessage = null;
            }

            var isConnectionLost = string.IsNullOrWhiteSpace(message);
            if (isConnectionLost)
            {
                // We lost the connection for some reason
                // Handle the situation
                return;
            }

            // Convert the received string into a string array
            var lines = new List<string>(message.Split("nr".ToCharArray(), StringSplitOptions.None));

            var lastLine = lines.LastOrDefault();
            var isBufferFull = !string.IsNullOrWhiteSpace(lastLine);
            if (isBufferFull)
            {
                trailingMessage = lastLine;
                lines.Remove(lastLine);
            }

            foreach (var line in lines)
            {
                if (string.IsNullOrWhiteSpace(line))
                    continue;

                ProcessIncomingMessage(line);
            }

            // Start listening for the next message
            ReceiveMessage();
        }

Now we just have to implement the ProcessIncomingMessage-method. In this method we will parse the message and react to it accordingly. It may contain a private message from an another user, a server check to make sure we’re still alive or anything else mentioned in the IRC specifications.

As you can see, we presume that the incoming message is UTF8-encoded. Unfortunately we can’t be sure of this. The IRC-protocol doesn’t dictate on what encoding should be used and this may vary between the clients. The UTF8 is nowadays the most used encoding so we use it.

At this point we’re going to just echo the message into our Debug-output but we’re going to add some basic parsing logic in the future episodes of this tutorial series.

        private void ProcessIncomingMessage(string ircMessage)
        {
            Debug.WriteLine(ircMessage);

            // Future hook for handling the message in somewhere else.
            // It's most probably wise to put the parsing logic in some other class.
           if (IrcMessageReceivedFromServer != null)
               IrcMessageReceivedFromServer(this, new IrcMessageReceivedFromServer(ircMessage));
        }

Current functionality

Now we’re almost there. We can already connect to a server and receive messages from it. You can see this in action if you run our app with the new code we have added in this tutorial.

image

But, if you let the app run long enough, you’ll also notice that we get two additional messages. One states that there has been an error and the other is just an empty string. The first message is received because we didn’t follow the IRC-specification: The server wants us to identify ourselves before it continues. Because we didn’t, the server disconnects our connection and the empty string is received because of this. If we try to call the ReceiveMessage-method after receiving the empty string, we will get an exception because the connection isn’t open anymore.

The next step

The next step is to start following the IRC-specification and actually send messages to the server. We will go through of this in the next part tomorrow.

Source code

The whole source code for this tutorial is available from the GitHub.

Links