Pseudorandom Knowledge

SignalR: talk to your clients

In HTTP the browser is supposed to initiate all requests. This makes two way communication difficult. If the server wants to push data to the browser the browser has to poll for it. Possibly with the help of Server-sent events. There is a modern solution, however. It is called WebSocket. But even when it can be used all it provides is a low-level TCP connection.

SignalR takes care of both of these problems; It uses the best technology available and it provides a common easy-to-use interface regardless of the underlying connection. SignalR is intended to be used with ASP.NET and jQuery.

Counter: <span id="counter">-</span>
<button id="reset">Reset</button>

For this demonstration I want to have a counter that repeatedly counts up and a button to reset the it to zero. The magic is that the same number is to be displayed on all connected browsers.

<script src="~/Scripts/jquery.min.js"></script>
<script src="~/Scripts/jquery.signalR.min.js"></script>
<script src="~/signalr/hubs"></script>
[assembly: OwinStartup(typeof(SignalRTest.Startup))]
namespace SignalRTest
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

First there are some infrastructure on the browser and server that needs to be in place before SignalR can be used. The hubs script is created by SignalR, it does not exist physically.

$(function () {
    $.connection.counterHub.client.update =
        function (value) {
            $("#counter").html(value);
        };
    $("#reset").on("click", function () {
        $.connection.counterHub.server.reset();
    });
    $.connection.hub.start();
});

This is all the JavaScript necessary for this little demonstration. The first part defines an update function to be called from the server. The second part binds the button to a call to a reset method on the server.

public class CounterHub : Hub
{
    private static int value = 0;
 
    public void Reset()
    {
        Interlocked.Exchange(ref value, 0);
        Clients.All.writeTime(value);
    }
 
    public static void Increment()
    {
        Interlocked.Increment(ref value);
        IHubContext context = GlobalHost.ConnectionManager
            .GetHubContext<CounterHub>();
        context.Clients.All.update(value);
    }
}

On the server side is the reset method and a call to the update function. The update call is wrapped in a static method because it has to be called from outside the hub. This is the recommended way of doing it.

new Timer(o => CounterHub.Increment(), null, 0, 100);

I put this line in Application_Start in the Global.asax.cs file. It works for the demonstration. In a real scenario it should definitely be replaced with something more robust.