Practical .NET

Building a JavaScript WebSockets Client

Create a JavaScript client that works with a WCF 4.5 WebSockets service to receive continuous, ongoing updates from the service.

In my last two columns, I've looked at configuring and writing a WCF 4.5 Service. This service accepted an Order Id and then sent back the order's status. However, because I built the service using WCF 4.5's WebSockets support, the service isn't restricted to sending back a single result. Instead, the service could continue to update the client as the status in the order changed.

The first step in working with the service is to create the client-side object that represents it. You do that by instantiating the WebSocket object, passing the URL for your service. The only JavaScript library you need to reference is jquery-1.6.2. To open my service (called OrderStatus.svc in a site called WSSample) as soon as the page is ready, I could use this jQuery code:

var wss;
$(document).ready(function () 
{           
 wss = new WebSocket("ws://localhost/WSSample/OrderStatus.svc");

The ws protocol used in the URL is required.

As I noted in my last column, the service can access the URL used to open its connection. For instance, if I wanted to specify the city being used in all the requests I'll make to this service, I could pass that in the URL's querystring:

$(document).ready(function () 
{           
 ws = new WebSocket("ws://localhost/WSSample/OrderStatus.svc?city=Regina");

I'm now ready to issue a request to the single method in the service by calling the WebSocket's send method, passing a parameter. However, it's certainly possible that I could get to the line of the code that issues the request before the connection is opened—that will generate an error. To ensure that the connection is open and ready to use, check the WebSocket object's readyState property to see if it's set to the OPEN constant (defined in the WebSocket object).

This code checks to see if the WebSocket is open before calling the service's method passing an Order Id. If the connection isn't open, the code notifies the user:

if (ws.readyState === ws.OPEN)
{
 ws.send("A123");
}
else
{
 alert("Connection to service not yet open.");
}

It would make sense to put code like this behind the onclick event of a button that the user clicks on after selecting the Order to be monitored. However, if you want to call the service as soon as it's available, you can attach a function to the WebSocket's onopen event, which fires as soon as the connection's open. This code sends the Order Id as soon as the connection's available:

ws.onopen = function () 
{
  ws.send("A123");
};

When the service sends data to the client, the WebSocket object will raise an onmessage event and the data will be passed to the event. This code attaches a function to process that data to the WebSocket's onmessage event:

wss.onmessage = function (status) 
{
 $("#orderStatusInfo").append(status.data + "<br/>");
};

It's probably a good idea to wire up a function to the onmessage event before you call the send method on the WebSocket object. That will ensure you're ready to catch the data from the service before the service starts sending it. If, of course, you don't need any more information from the service, you can simply close the connection using the WebSocket's Close method:

wss.onmessage = function (status) 
{
 $("#orderStatusInfo").append(status.data + "<br/>");
 ws.close();
};

Hopefully, the author of the service will have written the service so it won't attempt to send data once the receiving connection's closed (I discussed how to check for that in WCF 4.5 in my previous column).

It's also possible that you'll call the service multiple times, for at least two reasons:

  • To change the parameters for the kind of data you're expecting (in my case, to change the order whose status you wish to monitor)
  • To make a second request to be processed in parallel with your first request (i.e., to monitor a second order while still monitoring the first Order), or to cancel processing the first order.

The problem is that, in WCF 4.5 at least, a second call won't be processed while the first call is still executing. So if your first request instantiated a loop that continuously monitored the order's status, your second request won't be accepted. To prevent the first request from blocking any subsequent request, your service will need to spin off process to another thread.

For instance, this code in the method that accepts request calls a method named SendOrderStatus (passing the OrderId) and then exits, freeing up the method for a second request. The SendOrderStatus method, presumably, monitors the order status and sends updates to the client:

Dim t As Threading.Thread
  t = New Threading.Thread(
    New Threading.ThreadStart(Function() SendOrderStatus(OrderId)))
  t.Start()
End Sub

If you do have an ongoing process, you'll want to terminate it when the client stops listening for results. The only way to do that, apparently, is to catch the exception raised when you attempt to send data through a closed connection.

In my next column, I'll be finishing this exploration by looking at a simpler way to create a WCF WebSockets service.

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