HTML5 Tutorial – WebSocket Server (on ASP.NET)

imageWebSocket, introduced of as part of HTML5, defines a full-duplex single socket connection over which messages can be sent between client and server.

WebSocket is not just another enhancement to HTTP. The WebSocket standard simplifies much of the complexity around bi-directional web communication and connection management.

While the client side coding is fairly straight forward, implementing WebSockets on some development platforms involves writing a large amount of code just to provide basic functionality. A lot of the functionality could and should be provided by the framework, leaving you to focus on your application.

There are several WebSocket server implementations. Here are are few that may help meet your needs.

Microsoft has implemented WebSocket on different places in Windows 8 and .NET 4.5 Stack. These include an implementation for WCF and one for ASP.NET and another with SignalR.

Each has trade-offs.

In this post, you’ll get an overall understanding on how you can get started on several platforms. I’ll provide an overview where you can get started should you want to build your own framework in WCF and ASP.NET.

And I’ll implement the echo service that will work with the client in my previous post using Microsoft.WebSockets.

Some Java Implementations

  • jWebSocket provides HTML5 based streaming and communication applications on the web. jWebSocket is an open source Java and JavaScript implementation of the HTML5 WebSocket protocol with a huge set of extensions.
  • Kaazing WebSocket Gateway, a Java-based WebSocket Gateway.
  • Netty, a Java network framework that includes WebSocket support.

JavaScript Implementations

  • node.js, a server-side JavaScript framework on which multiple WebSocket servers have been implemented. See Node.js & WebSocket – Simple chat tutorial with source on GitHub.
  • Socket.IO aims to make realtime apps possible in every browser and mobile device, blurring the differences between the different transport mechanisms. It is a client side JavaScript library that talks to a Signal.IO server written in node.js.

Microsoft Support for WebSocket

The following chart shows how various ways your software can work with the Microsoft stack. Microsoft’s native WebSocket support begins with .NET 4.5 and IIS 8 which requires either Windows 8 and Windows 2012.

If you want support for earlier operating systems, you’ll want to use a library such as:

(You might want to use SignalR for other reasons, such as automatic support for long polling if the browser does not support WebSocket.)

How It Works

Microsoft has built abstraction in the System.Net.WebSockets namespace on top of System.Net.HttpListener and IIS 8.0 WebSocket Protocol.

Underneath it all is HTTP listener, which is part of the networking subsystem of Windows operating systems. HTTP listeneris implemented as a kernel-mode device driver called http.sys (Hypertest Transfer Protocol Stack).

image

You will probably want to build out your WebSocket server application at a higher level than System.Net.WebSockets.

The web socket handler in the ASP.NET stack is a concrete implementation of abstract class Microsoft.WebSockets.WebSocketHandler, which I will demonstrate in a following section.

WCF services and clients can use the NetHttpBinding binding to communicate over WebSockets.

WebSockets in WCF 4.5

WebSockets will be used when the NetHttpBinding determines the service contract defines a callback contract.

For more information on how to implement a WCF service and client that uses the NetHttpBinding to communicate over WebSocket, see:

How to Implement WebSocket Server in ASP.NET

This section show how you can implement a WebSocket in an ASP.NET application. We’ll do in four steps:

  1. Set up IIS to support WebSocket
  2. Write our client app
  3. Implement an HTTP Handler
  4. Implement Web Socket Handler

Requirements

Before you start you need to enable WebSocket Protocol at the machine.

  • Application must be hosted on IIS 8 (available only with some version of Windows 8 – please note currently IIS Express currently does not work)
  • WebSocket Protocol feature installed (IIS option)
  • .NET 4.5 installed
  • A compatible browser on the client (IE10 or Chrome will 18 work fine at time of writing)

To insure IIS 8 is configured for WebSocket, search for Windows Features in Settings charm. Then open Turn Windows Features on or off, expand  Internet Information Services and expand World Wide Web Services and expand Application Development Features and activate both WebSocket Protocol and ASP.NET 4.5.

image

Windows 8 and Windows Server 2012 include the .NET Framework 4.5, so you do not have to install it separately on those operating systems.

 

Start Your Web Site

Use Run As Administrator to start Visual Studio 2012.

Begin by creating an empty ASP.net application called MyWebSocket. Create the project using File | New | Web Site… Then select ASP.NET Empty Web Site and a edit the name.

image

Add the NuGet package Microsoft.Websockets.

image

Set your app to run on your IIS server. Right click MyWebSocket project, click Properties. Select Web in the menu on the left, scroll to the Server section. Select Use Local IIS Web server, clear Use IIS Express checkbox.

image

Create The Client

Add an HTML Page to the project named index.html. Replace the text with the following code (that was slightly adapted from my previous blog post.)


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script type="text/javascript">
var wsUri = 'ws://' + window.location.hostname +
window.location.pathname.replace('index.html', 'ws.ashx');
var output;
var socket;
function init() {
output = document.getElementById("output");
if (window.WebSocket) {
// WebSocket supported
writeToScreen("WebSocket supported");
testWebSocket();
}
}
function testWebSocket() {
writeToScreen("CONNECTING TO " + wsUri);
socket = new WebSocket(wsUri);
socket.onopen = function (evt) { onOpen(evt) };
socket.onclose = function (evt) { onClose(evt) };
socket.onmessage = function (evt) { onMessage(evt) };
socket.onerror = function (evt) { onError(evt) };
}
function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket is on the air");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: ' +
evt.data.toString() + '</span>');
socket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' +
evt + " at " + evt.currentTarget.url);
}
function doSend(message) {
if (socket.readyState != WebSocket.OPEN)
return;
//if (socket.bufferedAmount < someBufferThresholdinBytes) {
// socket.send(message);
//}
socket.send(message);
writeToScreen("SENT: " + message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
// should probably use jQuery.ready here
if (window.addEventListener) {
// for W3C DOM
window.addEventListener("load", init, false);
} else {
// legacy IE
window.attachEvent("onload", init);
}
</script>
</head>
<body>
<h2>WebSocket Test</h2>
<div id="output"></div>
</body>
</html>

I have changed the end-point URI for the WebSocket to call. The code dynamically configures the code to point to my WebSocket server, even when it uses various ports during your development.


var wsUri = 'ws://' + window.location.hostname +
window.location.pathname.replace('index.htm', 'ws.ashx');

Implement Http Handler

Add a Generic Handler to the project named ws.ashx. To do this, right-click your project, click Add | Add New Item… to see the Add New Item dialog. Next type generic in Search Installed Templates box.

image

 

Click Generic Handler in the center panel, type in the project name and click Add.

Replace the text with the following code. We’ll implement MyWebSocketHandler in the next section.


using System;
using System.Web;
using Microsoft.Web.WebSockets;
namespace MyWebSocket
{
public class ws : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
context.AcceptWebSocketRequest(new MyWebSocketHandler());
}
}
}

Wherever there is an access within ASP.NET application to HttpContext instance, you can check if the request is of WebSocket -type:


if(context.IsWebSocketRequest){
// Do something
}

WebSocket is TCP based network stream which is different from common HTTP request which can be cached by HttpContext.

Before WebSocket communication is started it has to be initiated. Because WebSocket protocol is an upgrade to HTTP, the spec requires using of HTTP to initiate WebSocket protocol.
Exactly this HTTP request is defined as WebSocketRequest. After successful completion of this request, WebSocket full-duplex is established.

Implement WebSocketHandler

Finally, write a class that handles our business logic in our WebSocket connection. Add a new class to the project named MyWebSocketHandler.

Replace the class with the following code:


using System;
using Microsoft.Web.WebSockets;
namespace MyWebSocket
{
public class MyWebSocketHandler : WebSocketHandler
{
private int clientsConnectCount;
public override void OnOpen()
{
clientsConnectCount++;
}
public override void OnMessage(string message)
{
this.Send(clientsConnectCount + " " + message);
}
}
}

Communicate with Your Client In Your WebSocket Handler

I derive my class from Microsoft.Web.WebSockets.WebSocketHandler and then override one or more of the virtual methods:


public virtual void OnClose();
public virtual void OnError();
public virtual void OnMessage(byte[] message);
public virtual void OnMessage(string message);
public virtual void OnOpen();

You add functionality to your handlers by overriding the virtual methods.

  • OnOpen should be used to perform any kind of initialization. This can be for example initialization of variables which will hold running sessions.
  • OnClose should be used clear the state of the handler when closed. Note that when OnClose is called, the handler is still running. That means, you can send messages within this methods to any connected client. For example this message can be something like: “bye – I’m going offline”.
  • OnMessage should be overwritten, because this is the place where to catch messages sent from WebSocket clients.

You can also communicate directly with the client.

  • Send is solely used to send the message to dedicated client. You can send byte array or a string.
  • Use Error to communicate errors and to pass exceptions to your client.
Maintaining Client List

The architecture of Web Sockets in ASP.NET provides one WebSocketHandler instance per client. For example, if you implement the browser-multiplayer-game every player will get its own WebSocketHander instance.

This means you can communicate with each individual member.

You can also maintain a list of clients using Microsoft.Web.WebSockets.WebSocketCollection class.

For example, if you included a user name in the WebSocket’s initial query string, you can maintain a list of clients and Broadcast to the entire collection:


public class TestWebSocketHandler : WebSocketHandler
{
private static WebSocketCollection clients = new WebSocketCollection();
private string name;
public override void OnOpen()
{
this.name = this.WebSocketContext.QueryString["name"];
clients.Add(this);
clients.Broadcast(name + " has connected.");
}
public override void OnMessage(string message)
{
clients.Broadcast(string.Format("{0} said: {1}", name, message));
}
public override void OnClose()
{
clients.Remove(this);
clients.Broadcast(string.Format("{0} has gone away.", name));
}
}

Alternative Way to Initialize Your Handler

Or you could initialize your handler with information from the context provided in the ProcessRequest method your http handler. For example


public void ProcessRequest(HttpContext context)
{
if(context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(
new YourWebSocketHandlerImplementation(context.Cookies[“UserName”)));
}
}

and then in YourWebSocketHandlerImplementation class, you can get the username passed in as the argument to the constructor.


public class YourWebSocketHandlerImplementation : WebSocketHandler
{
private static WebSocketCollection sessions = new WebSocketCollection ();
public string player;
public YourWebSocketHandlerImplementation (string argument){
player = argument;
}
public override void OnOpen(){
sessions.Add(this);
sessions.Broadcast(“New player joined the game”));
this.Send(“Welcome new player”);
}
}

Running You App

Run your app. When you make changes, you may need to restart your project.

image

Thanks

Special thanks to Alex MacKey for his article Websockets with ASP.net 4.5 and Visual Studio 2012 where you can also find Q&A about how to help debug common developer issues in building your first WebSocket app.

References

WebSockets on WCF
WebSockets on ASP.NET
SignalR
SuperWebSocket

Sample Code

Sample code is available in the DevDays GitHub repository. See https://github.com/devdays/html5-tutorials