Practical .NET

Building a Simpler WebSockets Service

Peter pays a final visit to the WCF 4.5 WebSockets implementation to take advantage of the WebSocketService class and build a service in six lines of code (not counting configuration and client-side code, of course).

Over my last three columns, I've been exploring how to build a WebSockets service and a JavaScript client that can access the service (for why you'd want to do this, see my first column.)

I've built the service by configuring a duplex WCF service using the new bytestream encoding format included with WCF 4.5, and the standard WCF structure that uses an interface and SVC file with a code-behind file. While it all works and is compatible with the way other WCF services are built, the implementation required you to deal with byte arrays and a variety of low level issues.

WCF 4.5 has an alternative way of implementing a WebSocket Service that allows you to create a "Hello, World" WebSocket service in six lines of code (though, of course, you're free to add ore code to create more complex services). You don't need an interface file and you don't need an SVC file. To use this simpler alternative you do, however, still need Windows 8 configured as  described in my first column. And your first steps are still to create a Web application and use NuGet to add the Microsoft.WebSockets package to your project. You'll also need to add references to the both the System.ServiceModel.Activation and System.ServiceModel libraries, if they aren't already present.

With your project configured, you create your service by adding an ordinary class file to your application and have it inherit from Microsoft.ServiceModel.WebSockets.WebSocketService. You can then override the methods in this class that you want to use. To handle communication with the client, you can add code to the following methods:

  • OnOpen: called when the client first contacts the service
  • OnClose: called when the client closes its connection
  • OnError: called when an error occurs
  • OnMessage: called when the service receives a message from the service. There are two versions of this method: one that's called when the client sends an array of bytes, and one that's called when the client sends a simple string

To send a message to the client, you use the class' Send method. Like OnMessage, there are two versions of the Send method: one that sends a byte array, and one that sends a string value. These six lines override the string version of the OnMessage method, to send the message received from the client back to the client with the word "Hello" tacked on the front:

Public Class SimpleWS
  Inherits Microsoft.ServiceModel.WebSockets.WebSocketService
    
  Public Overrides Sub OnMessage(message As String)
        Me.Send("Hello, " & message)
  End Sub

End Class

As with the WebSockets implementation from previous columns, you can access any querystring parameters passed in the URL that the client uses to open the service -- just use the class' QueryParameters collection. Using the class' WebSocketContext object you can also retrieve any cookies that the client passes, look at the URL the client used to contact the service, and access other useful information. This example checks the querystring to see if a value was provided for the name "city" and if the URL used to call the service contains the string localhost:

If Me.QueryParameters("City") = "Regina" AndAlso
   Me.WebSocketContext.RequestUri.
          AbsolutePath.Contains("localhost") Then
          
Supporting the Service
While the service class is simple, you do need to add two additional pieces of support to your site. You must add a class to your site that inherits from ServiceHostFactory and override its CreateServiceHost method to add a WebSocket endpoint to your site (this eliminates the need for any configuration elements in your config file). In the the CreateServiceHost method, instantiate a WebSocketHost from the parameters passed to the method, call the host's AddWebSocketEndpoint method, and return the host:
Public Class PHVHostFactory
  Inherits System.ServiceModel.Activation.ServiceHostFactory

  Protected Overrides Function CreateServiceHost(
          serviceType As Type, baseAddresses() As Uri) 
              As ServiceModel.ServiceHost

    Dim host = New Microsoft.ServiceModel.WebSockets.
                 WebSocketHost(serviceType, baseAddresses)
    host.AddWebSocketEndpoint()
    Return host

  End Function

End Class

You also need to add a ServiceRoute to your site's RoutingTable in the Application Start event of your site's Global.asax file. The ServiceRoute ties the URL for your service to an instance of your factory class and the type of your service class. This example creates a URL for the service that includes the string SimpleOrderStatus (e.g., if the site's URL is http:www.localhost.com/MySite, this routing will create a URL for the service of ws:www.localhost.co/MySite/SimpleOrderStatus):

Sub Application_Start(
  ByVal sender As Object, ByVal e As EventArgs)

  Dim phvhf As New PHVHostFactory()
  Dim sr As New ServiceModel.Activation.ServiceRoute(
              "SimpleOrderStatus",
              phvhf,
              GetType(SimpleWS))
  Routing.RouteTable.Routes.Add(sr)

End Sub

Thanks to the ServiceHostFactory, the only system.serviceModel settings you need in your Web.config file are the ones to ensure that aspNetCompatibilityEnabled is turned on so that you can use routing:

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
Calling the Service
The JavaScript to call this service is identical to the code I used in my last column to call the more complicated SVC-and-interface WebSocket implementation. You create a WebSocket, passing the URL for your service (this example includes a querystring with a value for the name "city" that can be retrieved in the service's OnOpen method using the QueryParameters collection):

<script>
  $(document).ready(function () 
  {
    wss = new WebSocket(
     "ws://localhost/SimpleWebSockets/SimpleOrderStatus?city=Regina");

Once the WebSocket object is created, you need to register a function to execute whenever the service sends you a message, by attaching the function to the WebSockets onmessage event. This example catches the message and uses jQuery to append the message to an element with an id of orderStatusInfo somewhere in the page:

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

Once the socket's successfully opened, you can start sending messages to the service. This example ties a function to a button's onclick event that sends the string "Peter" to the service:

<input id="Button1" type="button" 
       value="button" onclick="callservice();"/>

<script type="text/javascript">
function callservice() 
{
  if (wss.readyState === wss.OPEN)
  {
    wss.send("Peter");
  }
  else
  {
    alert("Unable to contact service");
  }            
}
</script>

When the service sends a response message, the onmessage function will catch the message and insert it into the page.

As I said in my first column, I think that WebSockets is a critical technology for any SOA. My only regret about WCF 4.5's implementation of WebSockets? That it's only currently available on Windows 8. Hopefully, that will change.

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