Skip navigation.

A Better, Stronger Home PageAll recent postsStop Image Flicker With Cache-Control Extensions

Count Your Online Visitors

How do you count people visiting my site at any given time? This question, in different variations, comes up in newsgroups quite often. There's really no canned answer. One good way of doing it is to log all sessions in a database table, and give each session a reasonable timeout. The web application will need to update the timestamp of its respective session on each page request. For example, if a visitor didn't browse from page to page for 5...10 minutes it's quite reasonable to assume he/she is gone.

This is half of the problem. The other half is clean out expired sessions. It's actually a tricky one. For example, you could run an NT service and have it wake up, say, every 10 minutes, and purge expired sessions, but it sounds like too much hassle. Anyone who has ever written an NT service knows what a pain they are. Besides, your hosting company will refuse to run your NT service. You could write a SQL job to do the same, but again your hosting company most likely will not bother.

Just recently I saw a reasonable solution presented by Rob Howard at TechEd 2004. Download slides and sample code under Blackbelt ASP.NET Controls. As you unpack samples, you will find HttpModule.cs in the Blackbelt_AspNet_Demos\BlackbeltBLL folder.

Rob showed how to wire a database cache dependency with the help of an HttpModule. The code is simple yet elegant. My spin-off is listed below:

public class SessionPurger : IHttpModule
{
  private static Timer    timer;
  private const int       interval = 1000 * 60 * 10;

  // ------------------------------------------------------
  public void Init(HttpApplication application) {
   // Wire-up application events
   if (timer == null)
   timer = new Timer(new TimerCallback(ScheduledWorkCallback), 
                application.Context, 0, interval);
  }

  // ------------------------------------------------------
  private void ScheduledWorkCallback (object sender) {
        HttpContext ctx = (HttpContext) sender;
        DataProvider.Instance (ctx).PurgeExpiredSessions ();
  }

  // -------------------------------------------------------
 public void Dispose() {
        timer = null;
 }
}

Implementation Details

An HttpModule fits the bill just right because it allows us to tap into the web application chain of events. We set up a timer and assign it a method to call at certain intervals. In my code ScheduledWorkCallback will be invoked every 10 minutes to purge expired sessions.

In ScheduledWorkCallback I call a stored procedure (via my Data Access Layer) which extends expiration of the current session by another 10 minutes. I track each session by its SessionID which is guaranteed to be unique. SessionIDs are 15 characters in length.

Gotcha #1: Threading

There's one very important thing to note here. Let me quote MSDN first:

Use a TimerCallback delegate to specify the method that is called by a Timer. This method does not execute in the thread that created the timer; it executes in a separate thread pool thread that is provided by the system. The TimerCallback delegate invokes the method once after the start time elapses, and continues to invoke it once per timer interval until the Dispose method is called.

Pay attention to the fact that ScheduledWorkCallback is called on a separate thread. I figured out the hard way that if you ever need to call HttpContext.Current it won't be there. That's why you coax it from the sender parameter.

HttpContext ctx = (HttpContext) sender;

Gotcha #2: Session IDs

If you store nothing in your Session a new SessionID is issued on each page request. If it happens the visitor stats are skewed really bad. You either need to store something in Session (not pretty) to trigger session state serialization or include a global.asax file in the project and make sure it has Session_Start in there. Presence of Session_Start does the trick. I always advocate dropping global.asax if you don't use it for anything, but this is one of those times when you need it.

Accuracy

A couple of words about accuracy. This solution ain't perfect. It's pretty good, but not 100% accurate. I don't know of a 100% accurate solution to this problem. But it comes pretty close.

Alternative Approaches

Why not use Session_OnEnd in global.asax? Session_OnEnd has too many what-ifs and is pretty unreliable. I wouldn't bank on it. It works only for InProc sessions. Also, it's a carryover from the classic ASP and is, essentially, a fudge.

I'm sure there other alternatives out there. Anyone?

Comments

Comment permalink 1 Liong Ng |
Do you see any issues in creating the timer in the Application_Start
event handler (see below) instead of your implementation?

protected void Application_Start(Object sender, EventArgs e)
{
m_timer = new Timer( new TimerCallback(ScheduledListDevices), null, 0, 3600000 );
}
Comment permalink 2 Milan Negovan |
Not really. I've never tried it though.
Comment permalink 3 Bipin |
Hi.

I want to put a counter of visitor on my site's home page. Any one who opens url will increment the counter. Hou I can do this. Any one can provide me solution? Please give me solution if you have?
Thanks
Comment permalink 4 Bob |
Hi, This is really cool. Ill have to try this when I have more time.
Comment permalink 5 Ravi |
Really good and useful
Comment permalink 6 Dragon |
I was looking for an online user counter but I couldn't find it.
I came across this and I want to help.

I'll just give the simple idea.
On your session_start use the folllowing code
begin

HITCOUNT_NUMBER := Application.Item['hit_count'] as integer; //reads

HITCOUNT_NUMBER := HITCOUNT_NUMBER+1; //increases

Application.Item['hit_count'] := HITCOUNT_NUMBER; //updates

end;

// now you can get this number from anywhere in your website you wish by using Application.Item['hit_count'] method.

//this is going to zero everytime you restart your website, so I suggest using database or xml "update TBL_COUNTER set COUNTER = COUNTER+1" so you won't lose it. if you have more than 200 online users, or your applications needs performance enhancement, I suggest you updating the number to +1 on a system.threading method

//Hope I helpd :)
Comment permalink 7 Vijay Karla |
Hi..
Is there any way to display online user names (suppose i have user names in session)?
Thank u in advance..
Regards Vijay Karla
Comment permalink 8 WhiteSites |
Actually having a live visitors counter is not that hard. We are doing it by setting both a session and a cookie on each visitor, Then we store some basic information about them ( IP, hostname, lastvisit, user agent, ext )in a MySQL table. As they browse our site we refresh their lastvisit date. We display how many visitors for the last 24 hours. With each request any visitors that have a last visit of more than 24 hours are deleted from the table. Live stats would be just matter of counting how many people have a lastvisit within the last 5 minutes, 30 seconds, whatever you want.

Granted this way does cause a DB call for every page request, but it works. The great thing is you can use distinct in MySQL to find how many unique visitors you have had by using the IP. It also gives you a heads up when bots are crawling your site, or stealing you content, because they can't use cookies and will recreate sessions on each request. This will cause your visitors count to climb very fast. There is really no reason that a real person would not have either cookies enabled or be able to maintain a session. This could allow you to detect spambots and capture their IPs for blacklisting. Of course make sure to let the googlebots browse freely.

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

TrackBacks

Sorry, TrackBacks are not allowed.

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):