So you've got your killer website up and running, and you're wanting to add a few little "perks." One of the hottest trends today is to show an "Active User Count" that shows exactly how many people are currently connected to your website. Many articles have already been written showing exactly how to do this using the global.asa file, so I won't even bother to explain that method. ASP 101 has a great article explaining the global.asa method of counting active users.
What I'd like to do is show you how to not use the global.asa file. I'm not saying using the global.asa file is bad--far from it. If you can use it then please do; it will probably yield a bit better performance. But there are many reasons why you might not want (or simply can't) use a global.asa file. Perhaps your host does not allow a global.asa. Maybe your site is hosted in a subdirectory of an existing site, and they won't set up an IIS application for you, so a global.asa file will do you no good. Or you might even not want to mess with an existing global.asa file, or not have access to do so. Personally, I needed to have this functionality for a web application I've been building (shameless link) and one of my design goals was that the Active User Count would NOT require use of a global.asa. I didn't want to bother with helping webmasters try to merge my code in with an existing global.asa, or guide them through setting up an existing IIS application so they could use my global.asa. Many of them couldn't even if they know how, because their web host doesn't allow it. So, this is the solution.
Quick Explanation
To make a manual Active User Count, we have a few options about where to store the active users on the server. We could store them in a database, in a text file, or in server memory in an Application variable. I chose to go with Application variables. Databases may be fast, but I don't like to query a database on *every* hit to a page unless absolutely necessary. Text files could get cumbersome, and I don't want to worry about file permissions. So the logical choice is Application variables. Application variables are very fast to access, since they reside in the web server's memory, and are available on just about every ASP web host out there.
I make use of three Application variables: one that holds the number of online users, another that holds a list of unique users and the last time of their activity, and the final variable remembers when the last cleanup occurred. We'll use the SessionID for a unique key of each user, and the simple Now() function to tag each SessionID with the time of last activity. So, without any more explaining, here is the "LogActiveUser" function:
LogActiveUser Routine
Sub LogActiveUser Dim strActiveUserList Dim intUserStart, intUserEnd Dim strUser Dim strDate
So let's take this line by line. First, we are copying the Active User list into a local variable. We are going to be doing lots of string manipulation, and we don't want to make a call out to the Application Variables collection every time we need to inspect the Active User list. Then we need to check to see if this user is already in the list. We do this by the simple Instr call to see if his SessionID exists. If it doesn't, its easy--we just add one to the ActiveUsers parameter and add this new SessionID and current time to the Active User list. If the SessionID already exists, we've got a bit more work to do. We've got to find this user in the list and update his time stamp.
The ActiveUserList variable will look something like this at any one time:
So we need to extract the user that we're looking for, change his timestamp to Now(), and insert him back in the list. Pretty easy to do. First we find the beginning of the user by searching for SessionID. Since the users are delimited by a pipe "|" symbol, we just search for the pipe starting with the beginning of the SessionID to find the end of the User's timestamp. Now that we have the beginning and the end, we can just replace this entire "chunk" of the string with the SessionID and current timestamp. This will effectively "reset" the user's timestamp. But, how do we clean up ActiveUsers as their timestamps get old? Let's look at the ActiveUserCleanup routine.
ActiveUserCleanup Routine
Sub ActiveUserCleanup Dim ix Dim intUsers Dim strActiveUserList Dim aActiveUsers Dim intActiveUserCleanupTime Dim intActiveUserTimeout
intActiveUserCleanupTime = 1 'In minutes, how often should the ActiveUserList be cleaned up. intActiveUserTimeout = 20 'In minutes, how long before a User is considered Inactive and is deleted from ActiveUserList
If Application("ActiveUserList") = "" Then Exit Sub
If DateDiff("n", Application("ActiveUsersLastCleanup"), Now()) > intActiveUserCleanupTime Then
For ix = 0 To UBound(aActiveUsers) If DateDiff("n", Mid(aActiveUsers(ix), Instr(1, aActiveUsers(ix), ":") + 1, Len(aActiveUsers(ix))), Now()) > intActiveUserTimeout Then aActiveUsers(ix) = "XXXX" Else intUsers = intUsers + 1 End If Next
Let's look at this routine line by line. The first thing we do is set some internal variables that decide when to cleanup, and how long before a User is considered Inactive. The variables intActiveUserCleanupTime and intActiveUserTimeout control these times, respectively. If the ActiveUserList is blank, we have nothing to do here, so Exit Sub. If the difference (in minutes) between the time we last ran the cleanup procedure and the time right now is greater than intActiveUserCleanupTime, let's run cleanup (this is better for performance--we only want to run cleanup every minute or so, not every time the calling script is hit). Immediately set the ActiveUsersLastCleanup variable to Now, so if another script calls this procedure while we are doing our work it will think that it is not yet time to run cleanup. Now we will copy the ActiveUserList to a local variable for increased performance and convert it to an array so we can loop through all the users. We loop through each user, and if we find a timestamp that is older than the intActiveUserTimeout variable, we will replace the entire User with "XXXX", and delete him later (we must do this because VBScript does not natively support any advanced array handling like deleting or inserting elements). Now that we've marked inactive users, we'll convert the Array back to a string. We end the string with our delimiter "|" to make the next step easier. Replace any occurences of "XXXX|" that we find to "", effectively deleting the user from the ActiveUserList. Now its time to store the variables back into the Application variables.
Getting it up and running with your Site This should be relatively easy. Just put these functions in a file and include it with any pages that will need to log Active Users and show an Activer User Count. So your script might look something like this:
<%
Call LogActiveUser() Call ActiveUserCleanup()
Response.Write "There are " & Application("ActiveUsers") & " active users currently online."
%>
Shameless Plug If you would like to see this code in action, head over to toastforums.com. See the "Current Online Forum Users" up in the right corner? That is using this exact code! Oh, and if you need a free ASP message board for your website, try Toast!