Practical .NET

WebSockets for Faster, More Scalable Ajax Applications

If you want to call a service you can—but the service can't call you back. WebSockets offers the potential for real, two way communications -- and it's as simple as calling a Web Service.

The idea behind WebSockets is simple: the client requests a TCP socket to communicate with the service. Once the socket's created, the client and the server can use it to send messages to each other. WebSockets' impact  should be tremendous, both in improving the performance of Ajax applications and their scalability. There are two reasons for this: First, WebSockets uses TCP (better performance than HTTP); Second, WebSockets implements two-way communication between a consumer and a service. For Ajax, that two-way communication isn't just better, it's new.

Currently, with Ajax, all communication is initiated by the consumer—the service can't call the client. With WebSockets, the service can send data to the client whenever the data is ready, eliminating the need for client to continually check if there's anything waiting for it at the service. Two-way communication substantially reduces the demands on services, improving their scalability. Two-way communication also enables more powerful Ajax applications that can, for instance, accept notifications from a service when something has changed at the service.

However, the protocol hasn't been finalized (though the JavaScript API probably is), and not all browsers support the API yet. To play with WebSockets in IE 9, for instance, you'll need to download the WCF WebSockets Prototype from Microsoft's HTML5 Labs. That means this column is an instalment of IMpractical .NET: WebSockets is a technology that's not quite ready for prime time, yet. But in the near future, if you're going to create Ajax applications, you're going to want to use WebSockets. And while the focus is on implementing this technology in Web browsers and making it available through a JavaScript API, there's no reason any type of client couldn't use this technology to communicate with a compatible service.

One final note: Since this is new technology in a pre-release version (at least for Internet Explorer), take this code as a guideline only.

Client-side Processing

A number of support files are required to create a WebSockets client for IE (e.g. JavaScript libraries, XML security files), and there's no template in Visual Studio's Add Project dialog to generate that project. To experiment on the client side, your best bet is start with one of the sample applications that comes with the Prototype download. That will free you to concentrate on experimenting with the JavaScript API.

The new API revolves around the WebSocket object. When you instantiate a WebSocket, you pass a URL that specifies the service you'll connect to (you can optionally pass an array of protocol names and, if the service doesn't support any of those protocols, the service can reject the request). The standard protocol is ws and the URL should end with a resource name. This example specifies the customer resource and includes an explicit port number:

wskt = new  WebSocket('ws://myserver.com:1867/customers'); 

The resulting WebSocket object has a readystate property that reports the status of the connection (Connecting, Open, Closing, Closed). More usefully, the WebSocket object fires an onopen event to which you can tie a function. You can use that to trigger some initial processing (assuming you need to do any—your client might just wait for messages from the service). To send a message to the service to which you've connected, you use the WebSocket's send method to send strings, a binary large object, or a fixed-length binary array. This code uses jQuery to retrieve the value from an element with an id attribute to set to CompanyId, and sends that value to a service (after passing the value through the JSON object's stringify method):

wskt.send(JSON.stringify($("#CompanyId").val()));

The send method actually just queues up your transmissions -- you're not guaranteed that your message is actually being delivered (at least, not immediately). To find out if your messages aren't getting through, you can check the WebSocket's bufferedAmount property to determine how much data is still waiting to be sent.

To receive messages from the service, you attach a function to the WebSocket's onmessage event (the API also supports the new DOM3 Event model). That function can accept a single parameter which may a string, a BLOB or a fixed length binary array. This code wires up a function that checks to see if the returned message is a string and, if so, uses jQuery to update an element with an id attribute of CompanyName:

 wskt.onmessage = function (incoming) 
{
var data = JSON.parse(incoming);
if (typeof data == 'string')
{
$("#CompanyName").val(data);
};
};

If you don't need a connection anymore you can close it, using the WebSocket's close method. This code checks to see if the WebSocket's readystate isn't already set to closed before calling the close method:

 if (wskt.readyState !==  1) 
{
wskt.close();
}

To reopen the connection you must create a new WebSocket object (there is no open method on the WebSocket).

WCF Server-side Processing

Ajax/JavaScript is only the client-side story (and the only part where the API is specified by an industry standard). Microsoft is handling the server side story through extensions to WCF. To accept and return results compatible with the WebSockets protocol, you'll need to create a new project and add references to these two libraries:

  • Microsoft.ServiceModel.Tcp.dll
  • Microsoft.ServiceModel.WebSockets.dll

The Prototype download includes these libraries. To add a reference to them to your project, select the Browse Tab in the Add Reference and look for the libraries in a subfolder of  the /Program Files/Microsoft SDKs/WCF WebSockets folder.

To create a client that handles requests and sends responses, create a class that inherits from Microsoft.ServiceModel.Websockets.WebSocketsService and override at least one of the OnMessage methods; either the version that accepts a string or the version that accepts a byte array. Since my client is sending a string, for this example, I override the version that accepts a string:

 <ServiceBehavior(InstanceContextMode  := InstanceContextMode.PerSession, 
ConcurrencyMode := ConcurrencyMode.Multiple)> 
   Class Customers
  Inherits Microsoft.ServiceModel.WebSockets.WebSocketsService
Public Overrides Sub OnMessage(Incoming As String)
  'code to process incoming message
End Sub

Since this one method will be accepting all of the messages from the client that uses this URL, you'll need to either design a message format that makes it easy to parse out your client's requests or have only one kind of message accepted at any URL. For instance, if the only message sent to the URL is a CustomerID and the result is always the CompanyName, you don't need to worry about the message format. If, on the other hand, you want this single URL to accept messages for retrieving, updating, deleting and inserting Customers, you'll need a more structured incoming message.

To send a message back to the client that's contacted the service, use the WebSocketsService SendMessage method. This example sends a message back to the client to indicate that the service is ready to accept messages:

 Me.SendMessage("Ready")

As you might expect, there are two versions of the SendMessage method: one that sends a string and one that sends a byte array.

The easiest way to test your service is to create a host as a Console Application (in real life, this would be a Windows Service). You must create a WebSocketHost, passing the type of the class you created to handle communication with the application. After you've created the host, call its AddWebSocketsEndpoint method, passing the URL you used in the client to contact the client. Finally, you should call the WebSocketsHost's Open method, followed by a call to Console.ReadLine to keep your Console in memory. In Visual Basic, this code would go into the Sub Main routine:

 Dim wsh As New Microsoft.ServiceModel.WebSockets.WebSocketsHost
 (Of Customers)
wsh.AddWebSocketsEndpoint("ws://myserver.com:1867/customers")
wsh.Open
Console.ReadLine

In C#, the code would go in the Program.cs file:

 WebSocketsHost<Customers> wsh = 
new WebSocketsHost<Customers>();
wsh.AddWebSocketsEndpoint("ws://myserver.com:1867/customers");
wsh.Open();
Console.ReadLine();

As you can see, both the JavaScript API and the WCF support for WebSockets is very simple: one object on both the client and the server, along with a handful of methods and properties. But the technology, if fully implemented, offers tremendous support for client-side applications in a service-oriented architecture. It won't replace WSDL+SOAP for creating services where interoperability is critical, but where the same team is creating the client and its services (i.e. Ajax), WebSockets will be a powerful tool.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube