In a recent article for Simple-Talk I tackled the deceptively simple task of counting the users currently connected to a given web application. The article is online here: Tracking Online Users. I illustrated the task with the easiest possible context of a web application that requires login. Surely, one can just increment and decrement the total count as the user logs in and out via the front page. As usual, though, the devil is in the details. What if the user just stops using the page and closes the browser? Or what if she just navigates away from the page and moves off to another site with different content? And, worse yet, what if I want to count users that are connected to public pages not placed behind the login curtain? This article discusses tracking online users with SignalR.
In the aforementioned article, I ended up using an implementation of the Ajax Heartbeat pattern. In a nutshell, the ‘heartbeat’ means that any given page repeatedly posts to a remote endpoint to show that it’s alive and kicking. I used this technique in combination with plain and simple login/logout counting and that seemed to work for me. I have now used that trick in production on a couple of sites and it delivered a solution very close to what I originally meant to have.
Was it a closed point? Well sort of until I ran again across the ASP.NET SignalR library. Like many of you I guess I am familiar with just one aspect of the library-its fabulous ability to simplify the process of pushing notifications from server back to clients. At the end of the day, ASP.NET SignalR uses an algorithm that, although more efficient in terms of implementation, is conceptually equivalent to long polling. ASP.NET SignalR works beautifully to notify client pages of server-side changes, and I always use it in that context. To my greatest surprise, though, ASP.NET SignalR also has a readymade infrastructure to count and track online users currently connected to a given SignalR hub. Not only can the library solve my biggest problem: that of telling you the total number of users, but it can, to some extent, also identify clients. This shouldn’t have been such a surprise to me, because ASP.NET SignalR needs to keep this information in order to send these clients push notifications.
Let’s find out more about the ASP.NET SignalR API to track and count online users connected to the application.
Trying Out Users Functions in SignalR
To set up SignalR, all you do is to link the Nuget package to the project and create the hub class. In the ASP.NET SignalR jargon, the hub class is the centralized console through which you handle the interaction between the server and all connected clients. Here’s the skeleton of a sample hub class.
1 2 3 4 5 6 7 8 |
public class SampleHub : Hub { // Public hub methods : // Overridable hub methods : } |
The hub class can contain as many public methods you wish it to have. A public method you define here is an endpoint you can call back from any server method while processing business tasks. Typically, hub endpoints are notifications pushed back to all connected clients. The hub class provides access to all clients at any time. Here’s the code that you need in order to gain access to all clients at any time.
1 2 |
var context = GlobalHost.ConnectionManager.GetHubContext<SampleHub>(); var clients = context.Clients.All; |
To be precise, the expression Clients.All gains you access to the list of clients but you can’t enumerate it because the actual class behind the All property is not actually enumerable. The SignalR framework makes intensive use of the dynamic features of C# and iterates internally to generate proxies to JavaScript functions defined on the client pages available through all connected browser sessions.
In other words, you can use Clients.All to notify server events to all connected browser sessions but you cannot query it for the number of connected users. There are other ways to do that.
Overridable Methods of the Hub Class
The Hub class you create in your applications to push events back to connected clients is not typically made only of public endpoint methods. There are a few overridable methods too and they just have to do with tracking connected users. Here’s a more specific skeleton of the sample hub class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class SampleHub : Hub { // Public hub methods : // Overridable hub methods public override Task OnConnected() { : } public override Task OnReconnected() { : } public override Task OnDisconnected(bool stopCalled) { : } } |
As you can see, there are three methods you can override: OnConnected, OnReconnected and OnDisconnected. As names clearly suggest, these methods are called just when browser sessions connect, reconnect or disconnect from the SignalR hub. By adding some code to these overrides you can easily extend the hub class to track user count too. Here’s an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class SampleHub : Hub { // Use this variable to track user count private static int _userCount = 0; // Public hub methods : // Overridable hub methods public override Task OnConnected() { _userCount ++; } public override Task OnReconnected() { _userCount ++; } public override Task OnDisconnected(bool stopCalled) { _userCount --; } } |
This is a very simple yet effective implementation of a hub that increments and decrements a counter variable as users connect and disconnect via any browser sessions and agents to the hub. The interesting thing that is going on here is just that you don’t have to track browsers and address-specific user agents. The SignalR framework intercepts any connections that are created or detached and notifies the hub class about it.
How then do we export this information to where we need it? That mostly depends on why you need to track online users. I ended up using the Heartbeat Ajax pattern in my previous article because I had to update a database record to annotate whether a given user was online or not. To achieve this goal with SignalR, yet another level of functionality in the framework must be unlocked. Before we get into that, though, let’s briefly see how you can display the current number of connected users in any relevant HTML pages.
SignalR is so useful because it lets you expose a specific endpoint for any server-side event for which it may be relevant to notify information to clients. This is the purpose of public hub endpoint methods. In this case, though, all we require is the fluctuating sum of the current users, and the changing number of users needs to be notified to your hub class independently from any server action and business logic tasks. In the end, the best place to place client push notifications is just the overridden methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public override Task OnConnected() { _userCount ++; var context = GlobalHost.ConnectionManager.GetHubContext<SampleHub>(); context.Clients.All.online(_userCount); } public override Task OnReconnected() { _userCount ++; var context = GlobalHost.ConnectionManager.GetHubContext<SampleHub>(); context.Clients.All.online(_userCount); } public override Task OnDisconnected(bool stopCalled) { _userCount --; var context = GlobalHost.ConnectionManager.GetHubContext<SampleHub>(); context.Clients.All.online(_userCount); } |
In the example, online is a JavaScript function that receives the currently detected number of online users.
1 2 3 |
sampleHub.client.online = function(count) { $("#onlineUsers").html(count); }; |
What if you want to find out more information about connected users?
Getting More about Users
The information available about users depends on the intended role of the user in the context of the application. If the user is logged in, then you can access the IPrincipal object of the HTTP context and read the identity name.
1 2 3 4 5 6 7 8 9 |
public override Task OnConnected() { var name = Context. User.Identity.Name; // Do something with the user name : return base.OnConnected(); } |
You get an empty string if the page is not behind a login façade. The user-tracking feature in ASP.NET SignalR is so attractive because you can easily count users regardless of whether they are logged-in or not. All that matters, in fact, is that the browser that hosts the SignalR client is connected to the hub. Each successful connection, however, is automatically assigned an ID by the SignalR pipeline. This ID is ultimately a GUID and is made programmatically available through the ConnectionId property on the Context property.
1 2 3 4 5 6 7 8 |
public override Task OnConnected() { var id = Context. ConnectionId; // Do something with the connection ID : return base.OnConnected(); |
The Context property is just a slight variation of the canonical HTTP context object and it also exposes much of the same environmental information you find in the HTTP Request object; primarily cookies, query string parameters and HTTP headers. In particular, you can get to know the user agent string and lay the ground for in-house analysis of the visiting users whether coming your way via mobile or desktop browsers.
1 2 3 4 5 6 7 8 9 10 |
public override Task OnConnected() { var id = Context. ConnectionId; var agent = Context.Request.Headers["user-agent"] ?? String.Empty; // Do something with the connection ID and user agent information : return base.OnConnected(); } |
If you reference a good server-side framework for device-detection, then you can immediately process the user-agent and start classifying mobile users and desktop users. For example, with the WURFL framework (see www.scientiamobile.com) you can have code as below to process the user agent and get a Boolean answer.
1 |
var isMobile = _device.GetCapability("is_mobile") == "true; |
What about Online Services?
The main point here is that ASP.NET SignalR allows you to achieve a very different task to online analytics services such as Clicky or Google Analytics. Both SignalR and the other solution covered in a past article using the Heartbeat pattern focus solely on tracking the number and names of users online. SignalR makes it trivial to find the user count. With a bit of more code around the hub class that it exposes, you can check whether a given user name-in case of logged users-is currently online.
Web analytics services, in contrast, can entirely monitor your web site, prepare reports and serve them through an ad hoc dashboard. You get an account, subscribe to the level of service you need and still have an API to work with programmatically in some of your pages. Through Clicky and perhaps Google Analytics you can still probably find out about the number of online users but that’s not the most important service you can get from those sites.
Comparing SignalR User-tracking to the Heartbeat Pattern
I started this article referring to another article I recently wrote for Simple-Talk: Tracking Online Users. This one can be considered to some extent as a follow-up for the other. What’s the difference then? This article is about tracking online users using the ASP.NET SignalR API; the other article was about using the Heartbeat Ajax pattern. Let’s briefly summarize the main differences.
Tracking Online Users with SignalR
ASP.NET SignalR is a framework with its own API for you to code. The Heartbeat pattern merely suggests how to solve a given problem. The Heartbeat pattern is not specifically about getting online users. It is, instead, about how a page within a client browser can tell the server it’s still alive. Based on this nugget of information, the pattern implementation can infer that a given user is online or not. As the name suggests, the Heartbeat pattern is also about pinging the server at a frequent interval, typically in the order of a few seconds. This may generate quite a bit of additional traffic and pressure on the server. ASP.NET SignalR works in much the same way, in the sense that it still does some form of polling to the server and subsequently adds pressure as well. However, underneath the covers of the framework, web sockets are used whenever possible or long polling. These are two techniques that are implemented transparently for the developers, and are effective in reducing the amount of additional traffic to the server. Nothing prevents you from using long polling or web sockets while implementing the Heartbeat pattern, but then again, that’s your decision and the burden of coding is on your shoulders. In addition, SignalR has endpoints to push server events of any kind to the client. If you use SignalR you can track online users easily and also be well positioned to tackle other, more common, programming tasks.
Load comments