Windows Phone 7 Sockets: How to open a connection
Advert: IRC7 is theIRC-client for Windows Phone 7. Learn more at www.softwaremk.org/irc
This is the part I 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.
In this first post we will investigate on how one can create and open a TCP-connection to a server.
Background
The Mango-update for Windows Phone 7 is arriving soon and one of the new features is the added support for sockets. WP7 Mango supports both the TCP and UDP protocols and to the delight of many people, its port-range hasn’t been restricted. So the Mango devices can connect to any IP address and to any port they want, as long as it’s open.
The System.Net.Sockets namespace
All the required components for creating a successful TCP-connection live inside the System.Net.Sockets –namespace.The namespace contains over 25 classes and enumerations but from those 25 only two classesstand out as an integral piece of the sockets-support: The Socket-class and the SocketAsyncEventArgs-class. Understanding these two classes is the key to understanding the sockets-support.
Unfortunately the System.Net.Sockets namespace is an odd bird when it comes to naming conventions and usability. Sometimes I’ve wondered how one namespace can be so different from the others. Maybe the fact that it deals with sockets has made the whole namespace to bend the rules. Maybe I just haven’t had the need to use those aspects of the socket-support which have forced to make the classes inside the namespace work strangely. But from what I’ve personally seen and experienced with the classes, the whole sockets-support could have been designed differently. And in this case “differently” means that instead of coming up with a new meaning for events and event arguments, it could have stick to the guidance.
We will focus only in these two classes because they are the central piece when creating our demo-application, the IRC-client. But when we start creating our application, we will cover most of the other interesting classes in the namespace.
The Socket-class
An instance of the socket-class represents our connection to the server. This instance will define the key aspects of the connection, like the selection between the TCP and the UDP-protocols. In our case we want to create the connection and keep it open. So we create the instance, connect to the server using the ConnectAsync-methodand then keep the socket-instance available for further work.
The socket-class offers the basic operations like ConnectAsync, SendAsync and ReceiveAsync. What makes it little complicated is that it doesn’t provide any feedback by itself if these operations succeeded or not. So you execute your work through the socket-class but you receive the feedback through a different route: With the help of the SocketAsyncEventArgs-class.
The SocketAsyncEventArgs-class
Where the socket-class comes short is providing any information on the events that happen with our connection. Instead we have to always rely on the SocketAsyncEventArgs-class to provide us the information we want. And even though the class has the “EventArgs”-postfix, it is up to you to create the instances of this class.
When you execute an operation with the Socket-class, you’ll have to provide the SocketAsyncEventArgs-instance for it as a parameter. The SocketAsyncEventArgs-instance will always raise a Completed-event when the operation has been executed. So, instead of providing the ConnectionCompleted, MessageReceived and MessageSent-events, you will always receive the Completed-event which you must know how to handle.
To differentiate between the different operations, the SocketAsyncEventArgs-class provides a LastOperation-property which is of type SocketAsyncOperation. This enum contains values like “Connect”, “Receive” and “Send”.
How to use the Socket-class and the SocketAsyncEventArgs-class
As mentioned above, you work with the Socket-class by creating an instance of SocketAsyncEventArgs and passing it as an parameter for the socket operation you want to execute. The SocketAsyncEventArgs will always raise the Completed-event when the operation has finished.
Here’s some pseudo-code which hopefully will make this more clear:
var myConnection = new Socket(); var socketOperationEventArguments = new SocketAsyncEventArgs(); socketOperationEventArguments.Completed += OnConnectionCompleted; myConnection.ConnectAsync(socketOperationEventArguments);
When the operation has finished, no matter what operation you executed, the SocketAsyncEventArgs will always raise a Completed-event. So, if you create a connection or receive a message or send a message, you will always receive the Completed-event. If you are re-using your SocketAsyncEventArgs-instances, like the MSDN-tutorial shows, you must always check the instance’s LastOperation-property for its current value and only then you will know if you received a new message or if the event was actually feedback for the “message send” operation. Fortunately there’s a better way.
The practical way for working with the Socket-class and the SocketAsyncEventArgs-class
If you’re familiar with the NHibernateyou may know that to get it up and running you create one instance of SessionFactory and keep hold of it. Then you create a new Session when ever you need to access the database. When your work with the DB is done, you get rid of the Session.
My personal recommendation is to treat the Socket and the SocketAsyncEventArgs classes in a similar fashion: Create the Socket-class and keep hold of it. Then create a new instance of SocketAsyncEventArgs when ever you need to do some work with the connection. And then you get rid of the instance.
It is possible to re-use the SocketAsyncEventArgs-class but I’ve found this to be cumbersome. It’s easier to start with a fresh instance every time. This means that you can attach a meaningful event handler for the Completed-event, like “OnConnectionCompleted” or “OnMessageReceived”. If you re-use the same instance every time, you must use a generic event handler like “OnOperationCompleted” where you must check for the LastOperation-property and route the handling to the correct method.
Creating the connection
Now that we understand the two central pieces of the Socket-namespace it is time to start out IRC-client project. To better understand the whole protocol one can study the available specs. But at this point it’s enough if you understand the basic characteristics of an IRC-connection:
- Client uses the TCP-protocol to connect to an IRC-server
- The connection must stay open as long as the client is used
- Messages are sent and received in plain text (except in the case of SSL connections)
To create a connection we must known the address of one of the IRC-servers. QuakeNet is one of the largest IRC networks out there and we will be connecting one of their servers during these tutorials. We will use the server address fi.quakenet.org.This server should allow connections from every part of the world but if it doesn’t you can use one of the servers listed in here.IRC-server are usually available in the port 6667, and this rule applies to fi.quakenet.org too.
During these tutorials we will create a class called IrcConnection which we will use as a wrapper around the System.Net.Sockets and the IRC-protocol. At this point we are only concerned of opening the connection to the IRC-server so here’s the code from which we will start moving forward:
public class IrcClient { public void CreateConnection(string serverAddress, int port) { } }
Like previously mentioned, an instance of the Socket-class is required to get things working. And also, we don’t want to lose our instance. So we create it and store it as a field inside the IrcClient. To create the instance we need to pass it few parameters:
- AddressFamily-enum: This enum specifies the addressing scheme for our Socket. Potential values include InterNetwork (IPv4), InterNetworkV6 (IPv6) and so on. In our case we will use the InterNetwork.
- SocketType-enum: From the MSDN-documentation, “The SocketType enumeration provides several options for defining the type of Socket that you intend to open.” When creating an IRC-client we will use the Stream-type.
- ProtocolType-enum: Last but not least you must make a selection between the TCP and UDP-protocols. In our case the selection is TCP.
With these selections we can create our instance of the Socket-class:
this.connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
As you probably noticed, you don’t pass the server’s address or portto the constructor. What makes things even more strange is that those parameters aren’t passed to the ConnectAsync-method either! This is where the SocketAsyncEventArgs-class comes into play. To create the connection, you must first instantiate a SocketAsyncEventArgs-object. You then tell it the server’s endpoint (address and port) and using this object, you can call the Socket’s ConnectAsync-method. When the ConnectAsync completes, your SocketAsyncEventArgs-instance will raise the Completed-event:
private Socket connection; private string server; private int serverPort; public void CreateConnection(string serverAddress, int port) { this.connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.server = serverAddress; this.serverPort = port; var connectionOperation = new SocketAsyncEventArgs { RemoteEndPoint = new DnsEndPoint(this.server, this.serverPort) }; connectionOperation.Completed += OnConnectionToServerCompleted; this.connection.ConnectAsync(connectionOperation); } private void OnConnectionToServerCompleted(object sender, SocketAsyncEventArgs e) { }
And that’s about it! If the provided server address and port were correct, your connection is now open and you can start sending and receiving message between your client and the server. The SocketError-property in SocketAsyncEventArgs can be used to check if the connection was opened successfully.
if (e.SocketError != SocketError.Success) { ... }
Source code
The whole source code for this tutorial is available from the GitHub.