First I would like to say thank you to David Fowler and all whom have contributed to the SignalR project. They have really created a fantastic library for real time web communication. I have been studying up on SignalR and have tinkered with it a bit in preparation for a up and coming project I have. Long story short, I want to inform users asynchronously of there completed requests for parsing or OCRing a PDF file. This posed a few issues.
Issue 1. The server used to parse the PDF file is separate from the web server.
Issue 2. The PDF parser is a Windows Service
Issue 3. Users do not want to receive e-mails.
Issue 4. Some users want to see all items processed
Okay, so it was a pretty easy decision what to use to send and receive messages. MSMQ; its free on windows, it is easy to implement in .net, it is asynchronous and it will scale if needed. That takes care of Issue 1 and 2 as I now have a Queue with which to send and receive messages. I am not going to go into detail on the MSMQ product as it is well documented. If you are interested in learning more about it I would suggest this MSDN article.
Now for issue 3; informing the user. SignalR seemed like a perfect solution. It uses a publisher/subscriber design pattern to allow multiple clients to connect to a hub and messages are pushed to the open client connections. The easiest way to begin using SignalR is to create an new MVC 4 template project in VisualStudio and add the SignalR NuGetPackage(Microsoft.AspNet.SignalR). SignalR is still in pre-release so you will need to change your NuGet package manager to "Include Pre-release" or use the package manager console and type:
Install-Package Microsoft.AspNet.SignalR -pre
Either way will work. Now the fun part. First we need to set up our MSMQ communication.
MSMQ
MSMQ is a receiver/sender model so we will first need to get messages in the queue. If you do not have MSMQ installed you will need add it as a windows feature (I am using Windows 7, Windows 8 has a similar interface):

Once installed you will need to open computer management expand the "Service and Applications" you should now see an "Message Queuing" section like the following:

Then create a new private queue for creating your messages (you can use a public queue but that requires an Active Directory connection):

The queue should not be transactional as transactions are not required. (You can read more about transactions in the aforementioned article.) I am just going to name it "test":

Now the queue is complete and you can added messages. I've created some helper classes that will perform some of the basic MSMQ interations. They will be included in the completed project.
Since we will not be creating an actual service we will need to create a page to add messages to the Queue. The easiest way to do this is to add a new model class and add an action to your controller. The model class will be something simple like the following:
[Serializable]
public class CompleteMessage
{
public string CompletionMessage { get; set; }
public bool Successful { get; set; }
public string SessionId { get; set; }
public string ErrorMessage { get; set; }
}
We will call the action CreateTestMessage:
public ActionResult CreateTestMesage()
{
ViewBag.Message = "Create a message.";
return View();
}
[HttpPost]
public ActionResult CreateTestMesage(CompleteMessage AddedMessage)
{
AddedMessage.SessionId = System.Web.HttpContext.Current.Session.SessionID;
MessageSender.SendMessage(AddedMessage, AddedMessage.SessionId);
//redirect to a fresh page
return RedirectToAction("CreateTestMesage");
}
Create a databound view for the action by right clicking inside the action and selecting "Add View...":

The selecting the CompleteMessage for the strongly typed view with a scaffold template of "Create":

You will need to remove the SessionId section from the generated cshtml as this will be populated in the action. You can now run the application and add items to the MessageQueue.
SignalR
Now to inform the user. First, we will need to add the appropriate hooks for SignalR (the Microsoft.AspNet.SignalR.Sample adds the hooks for you, but we wont be using that in this example. It is however on NuGet in PreReleases.) I created the SignalR messaging classes in a separate NameSpace/folder. First you will need to create the "Hub", the hub is how the server communicates back to the server (and visa versa in a WebSocket scenario). SignalR has really made it easy to create a hub. Depending on what the client needs to access, the hub can be very small. I have added a GetAllMessages() method to my hub, but I am not currently using it.
In order for the hub path to be mapped you will need to make one minor change to the Register Routes:
//required for signalR activation. This should come directly after the IgnoreRoutes
RouteTable.Routes.MapHubs();
This creates the default hub route "/signalr" this can be changed if you would like. This is the path used for Client/Server communication
The important part of the hub is the following:
[HubName("msmqHub")]
public class MsmqHub : Hub
The HubName attribute will be used to create a dynamic JavaScript/JQuery communication hub. This is what you will call from you client. If you run the application and look at the "hubs" generated JavaScript file, you will see how the communication is performed. I have highlighted some of the code generated from the created MsmqHub class:
$.hubConnection.prototype.createHubProxies = function () {
var proxies = {};
this.starting(function () {
// Register the hub proxies as subscribed
// (instance, shouldSubscribe)
registerHubProxies(proxies, true);
this._registerSubscribedHubs();
}).disconnected(function () {
// Unsubscribe all hub proxies when we "disconnect". This is to ensure that we do not re-add functional call backs.
// (instance, shouldSubscribe)
registerHubProxies(proxies, false);
});
proxies.msmqHub = this.createHubProxy('msmqHub');
proxies.msmqHub.client = { };
proxies.msmqHub.server = {
getAllMessages: function () {
/// <summary>Calls the GetAllMessages method on the server-side msmqHub hub.
Returns a jQuery.Deferred() promise.</summary>
return proxies.msmqHub.invoke.apply(proxies.msmqHub, $.merge(["GetAllMessages"], $.makeArray(arguments)));
}
};
return proxies;
};
signalR.hub = $.hubConnection("/signalr", { useDefaultPath: false });
$.extend(signalR, signalR.hub.createHubProxies());
Now the hub is only the first part. A class will need to be created to read from the MSMQ Private Queue. I have written some helper classes for communication with MSMQ so the Watcher class only has a few key parts.
1. Create the singleton for the class.
//this is the prefered method for setting up a singleton as of .net 4.0
private readonly static Lazy<MsmqWatcher> _instance = new Lazy<MsmqWatcher>(() => new MsmqWatcher());
2. Create the MSMQWatcher.
//create the message queue
private readonly MessageReceiver<CompleteMessage> _msmqReceiver = new MessageReceiver<CompleteMessage>(false);
3. Create the Hub Connection to the clients.
//create a connection to the SignalR hub clients
private readonly Lazy<IHubConnectionContext> _clientsInstance = new Lazy<IHubConnectionContext>(() => GlobalHost.ConnectionManager.GetHubContext<MsmqHub>().Clients);
4. Now that the hub and the watcher is created the last thing to setup is the client script
$(function () {
//create the hub
var msmqTicker = $.connection.msmqHub;
$.extend(msmqTicker.client, {
sendComplete: function (pdfComplete) {
//on send complete add to a tag in the dom
$("#messageRecieved").append(pdfComplete.CompletionMessage + "<br/>")
}
});
//start the client hub
$.connection.hub.start();
});
If you run the provided sample open 3 windows. One window in IE 9, one in a recent version of Chrome, Firefox or Safari, and the last in a browser of your choosing. The reason I wanted you to use IE9 is it uses a different method of server communication than the other browsers. The non IE Browsers listed support SSE (or Server-Sent Events). This is the prefered method of communication from server to client when there is no need to communicate from the client back to the server.
If SSE was not available SignalR would try Web Sockets. If Web Sockets was unavailable (as in IE 9's case) it uses a Comet programming technique called Forever Frame. The short definition of "Forever Frame" is a hidden IFrame that maintains a constant open connection to the server, this allows the server to send data back whenever it wants as the connection is never closed (in theory, in practice it will timeout). And finally, if all else fails, SignalR will default to Long Polling. NOTE: Long Polling will be used by IE for Cross Domain Support as Forever Frame cannot be used in separate domains.
Luckily, SignalR does all of the heavy lifting. You don't need to know how it is communicating with the client you just need to know that it is. Take the following screen shot:

IE9 is using Forever Frame and Google Chrome is using SSE, but to the end user it looks exactly the same.
Now, unfortunately, a few caveats.
- SignalR is still a pre-release and I have noticed on GitHub a few people are still having some issues with it, especially where Forever Frames is concerned.
- IE 10, Chrome, Safari and Firefox supports Web Sockets and Server Sent Events but IE 9 will need to use Forever Frame or Long Polling.
SignalR by default will use Server Sent Events which is the most efficient push technique but you can override the order in which the
transports occur. SignalR s definitely a fantastic edition to the .Net library, and one that I am looking forward to using professionally once it is out of pre-release.
All of the code use to create this article can be found in the following download:
MSMQSignalRTest.zip (13.45 mb)