SignalR and the Real Time Web

While working on a recent project, my manager came up to me with a design requirement. We had a process running on the server that was split into various sub-processes and tasks. Each time this process was invoked by a user, it was required that he received updates to its progress within a UI. He asked me whether this could be done with the help of async Ajax requests from the client (UI) to the server (process) at regular intervals. This was possible but felt very contrived. The system would not actually be real-time. Fortunately, during that time I had been reading up on WebSockets and the concept behind push notifications. To me, this seemed like a very plausible use case for the same.

The web works on the request/response paradigm of HTTP. A client loads a web page all interaction / communication is driven by the client through events. Around 2005, AJAX started to make the web feel more dynamic. Still, all HTTP communication was steered by the client, which required user interaction or periodic polling to load new data from the server. The server is unable to push data to the client in real-time if a request for it has not been made.

With web sockets, you can send messages to a server and receive event-driven responses without having to poll the server for a reply. The WebSocket specification defines an API establishing "socket" connections between a web browser and a server. In plain words: There is an persistent connection between the client and the server and both parties can start sending data at any time.

Problem was, I knew that Web Sockets were not compatible with IE 8/9, the browsers that our client app had to support.

It's then that I came across SignalR. ASP.NET SignalR is a library for ASP.NET developers that simplifies the process of adding real-time web functionality to applications. It provides a simple API for creating server-to-client remote procedure calls (RPC) that call JavaScript functions in client browsers (and other client platforms) from server-side .NET code. SignalR uses the new WebSocket transport where available, and falls back to older transports where necessary. SignalR is an abstraction over some of the transports that are required to do real-time work between client and server. A SignalR connection starts as HTTP, and is then promoted to a WebSocket connection if it is available. This enables it to be usable with older browsers, as was the case with us.

The two primary components in SignalR are hubs and persistent connections. Connection persistence is how the server can communicate with one or more clients. The vehicle to communicate with clients is a hub.

In the following example I will instrument an MVC4 application to use SignalR to send push notifications to an HTML page whenever a controller action is invoked.

Step 1

Create an MVC4 project in Visual Studio. (I am using VS 2012. I believe VS 2010 should also work for our example.)

Step 2

Right-click the project name and select Manage NuGet Packages. Search for Microsoft ASP.Net SignalR and install it along with all dependencies. The current version (at the time of writing this article) is SignalR 2.1.2.

Step 3

Right-click the project name and add a new folder named Hubs to it. Within this add a C# class called NotificationHub.cs. (You can go ahead and name it anything you wish.)

public class NotificationHub : Hub
{
	public void Activate(string content)
    {
    	Clients.All.notifyUsers(content);
    }
}

In order to use the Hub class you need to include the following:

using Microsoft.AspNet.SignalR;

The Hub allows us to communicate with / push messages to any client connected to it.

notifyUsers() is a method on the client. Keep in mind that we have not written the client yet.

Step 4

In the existing HomeController.cs file we will write a server side method that will be able to invoke a specified client side (JavaScript) function.

using Microsoft.AspNet.SignalR;
using MvcApplication1.Hubs;

MvcApplication1 here is the name of my project.

private void SendMessage(string message)
{		GlobalHost.ConnectionManager.GetHubContext<NotificationHub>().clients.All.sendMessage(message);
}

This means that the server side method, SendMessage(), when invoked, will call a JS function named sendMessage().

When we need to invoke a method on the client from outside of the SignalR Hub class we need to make use of the GlobalHost class. It gives us the Hub context through the IConnectionManager interface.

Now we can call this SendMessage() method from any of the Action Results. For example:

 public ActionResult Index()
 {
	ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
    SendMessage("Index action invoked.");
    return View();
 }

This means that when we navigate to the Index.cshtml page, the Index ActionResult will call the private SendMessage() function with the parameter "Index action invoked", which will in turn call the client side method sendMessage() with the same parameter. What this JS function does, we'll write later.

Step 5

In this step we will go ahead and create an HTML page that will contain the functions that the server method SendMessage() will invoke. Namely, sendMessage(). It will also contain the method notifyUsers(), which will be invoked by the Activate() method in the NotificationHub.cs file.

Add an HTML page to the project called SignalMonitor.html.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title></title>
</head>
<body>
	<div id = "target" class="display-label">
    	<ul></ul>
	</div>
</body>
<script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="Scripts/jquery.signalR-2.1.1.min.js" type="text/javascript"></script>
<script src="signalr/hubs" type="text/javascript"></script>
<link href="Content/Site.css" rel="stylesheet" type="text/css" />

<script type="text/javascript">
	$(function () {
    	var notificationHub = $.connection.notificationHub;
        notificationHub.client.sendMessage = function (content) {
        $("#target")
           .find('ul')
           .append($("<li></li>").html(content));
           };
        notificationHub.client.notifyUsers = function (name) {
        $("#target")
        .find('ul')
        .append($("<li></li>").html(content));
    	};
        
        $.connection.hub.start().done(function () {
        notificationHub.server.activate("SignalR Activated");
    	});
    });
</script>
</html>

Step 6

Now we add another C# file to the project called Startup.cs.

using Owin;
using Microsoft.Owin;


[assembly: OwinStartup(typeof(MvcApplication1.Startup))]
namespace MvcApplication1
{
	public class Startup()
    {
    	public void Configuration(IAppBuilder app)
        {
        	app.MapSignalR();
        }
    }
}

The Startup class is required for SignalR to run. This is because SignalR is built upon OWIN (Open Web Interface for .Net). OWIN is a set of specifications that form a level of abstraction between web applications and web servers. This implies that an application that follows OWIN specification can run on any server that implements the same. SignalR is part of Microsoft's Project Katana which represents the set of OWIN components that, while still open source, are built and released by Microsoft. Katana aims to lessen dependence on IIS / System.web by allowing applications to be self-hosted or hosted on a custom host or OwinHost.exe when all its processing features are not required. Moreover, you can define a pipeline of modules that are used during the request processing. An OWIN pipeline is a chain of OWIN compatible components through which a request passes. In our case IIS continues to be the host / server as we're using an MVC app to demo SignalR. The Startup class just includes SignalR in the pipeline.

So, how this works is:

  1. We build the project and run it with SignalMonitor.html at the Start Page. The $.connection.hub.start().done() method is invoked. This indicates that the SignalR connection to the hub has been established.This in turn calls the Activate() function on the server with the message "SignalR Activated". Activate() calls the client method notifyUsers() with the same message.

  2. notifyUsers() appends "SignalR Activated" to the <ul>.

  3. Now we navigate to our MVC app Index page in a separate tab in the same browser window. The ActionResult Index() method is invoked on the controller which calls SendMessage() with the message "Index action invoked.".

  4. This calls the client method sendMessage() with the above string and the same appends it to the <ul> as another <li> item.

In this way we see that the server (MVC controller method) is able to push a message to the client (HTML page) without the client polling it.This is what we were looking for, albeit with a more intricate and detailed implementation.

In my next write-up I'll talk about pushing messages to only specific clients from the server and not all clients connected to the hub.

You will find the code for this article SignalRTrial.zip at https://github.com/ritwikroy7/.Net. It contains some additional implementation that we'll discuss in the next article.

Ritwik Roy

Software Engineering Architect. I enjoy reading, writing, photography and art. You can get in touch with me at ritwikroy7@gmail.com. Twitter handle - @_RitwikRoy.

Amarillo, Texas