Like most of our topics, I'm covering this one due to
user demand for it. If you've been to any one of a few dozen ASP-based sites
recently, you've probably seen an example of the script we've going to cover in
this article. It's the little line at the top of these pages saying
something like "There are 28 users currently on this site!" It's
sort of cute and it gives you an idea of how many people are using the
site. I've even seen one go so far as to show you a list of where people
are on the site. While it's not something that we've chosen to implement,
there seems to be enough interest that we thought we should address it. Normally I'd cover a
topic like this in a sample, but since the bulk of this code needs to go
into your global.asa file, I thought I should probably give a more in depth explanation.
What are we counting?
It seems to be a silly question. After all, we
just said we were going to count active users. Well what exactly is an
active user? A connection using HTTP (the protocol of the web) isn't like other
connections. FTP, Telnet, E-mail, and most other types of connections are started when a
user first visits and ended when the user leaves. HTTP
doesn't work this way. The connection is made, the request for the page
is sent, and the connection is dropped. This makes getting the number of people currently "on" a site
rather difficult. You don't have a connection to everyone so you can't simply count them.
You also have no way of telling if they're going to come back and request another page or not until
they do or until you decide you've waited long enough that they aren't going to!
Here comes ASP with a solution!
The Session object. All the Session Object does is
provide us with a temporary storage area on the server for each
user. It keeps track of which user belongs to which storage area by using
cookies. These cookies never have their expiration property set and
as a result, expire as soon as you close your browser. Unfortunately, because
the Session object relies on cookies, if a visitor's browser doesn't support them or has them turned off,
each request is looked upon as a new session and can cause problems for a site
which uses a lot of Session objects or does a lot of processing in Session_OnStart.
It also causes a script like this to count every hit as a session! This is the main reason
we don't like this script too much and haven't used it. The good news is, that for the most
part, the fear associated with cookies has been somewhat lessened through user education and
the number of users who go out of their way to turn cookies off has decreased significantly.
ASA, I thought they were ASPs!
This brings our discussion to a very special file in
your web project: global.asa. Global.asa is the file that defines a web
application. It contains special scripts called event handlers.
There are four such scripts possible with the current version of ASP. These
are Application_OnStart, Session_OnStart, Session_OnEnd, and Application_OnEnd. The Application_OnStart script is
called when you first start the web application
by requesting the first .asp file from it. The Application_OnEnd occurs when
you shut down the web server
(but may not be called if the server freezes
up or loses power). As you might expect by now, Session_OnStart occurs when
a user requests their first ASP page. But when does Session_OnEnd run? The OnEnd event runs when the session ends which can occur
in a couple different ways. The first one is you can call the Abandon
method of the Session object which will kill the session
immediately. The second is you can wait for the session to timeout. This
will take however many minutes you've set the Session.Timeout property to. (The default is 20 if no one's
changed it.) Naturally, it's more desirable to call Session.Abandon and have the Session destroyed immediately and
free up the resources it uses instead of letting it use them for 20 minutes.
Now to our user count!
Like I mentioned earlier most of the code for this
particular script needs to go into your global.asa file. For reference,
I'm including a global.asa, which includes all the pieces needed to get this
script working, below:
Global.asa:
<SCRIPT LANGUAGE="VBScript" RUNAT="Server">
Sub Application_OnStart
' Set our user count to 0 when we start the server
Application("ActiveUsers") = 0
End SubSub Session_OnStart
' Change Session Timeout to 20 minutes (if you need to)
Session.Timeout = 20
' Set a Session Start Time
' This is only important to assure we start a session
Session("Start") = Now
' Increase the active visitors count when we start the session
Application.Lock
Application("ActiveUsers") = Application("ActiveUsers") + 1
Application.UnLock
End SubSub Session_OnEnd
' Decrease the active visitors count when the session ends.
Application.Lock
Application("ActiveUsers") = Application("ActiveUsers") - 1
Application.UnLock
End Sub
</SCRIPT>
So what exactly does all that do? Well lets take it one section at a time.
Application_OnStart
The first section of code goes into
The Application_OnStart event handler. Like I mentioned earlier, this is
run only when the first user hits your web server. What we do in this
section is create a variable, which has Application scope (which means all
Sessions can access it and share the same value), named ActiveUsers and
set it's value to 0 since at this point there ar no users on our
site. Variables of this type are usually just called an
application variables.
Session_OnStart
This is the part of the script which
does the real work of keeping track of counting new users. It's pretty
simple, but we do two things you may not be expecting. The first thing we
do is set the length of time we want to wait from the time a request is made
until we kill the session. This is important, because if you set it to 0
then every request would be considered a session, but on the other hand, the
longer you set it for, the more memory and resources get used by your server
keeping track of sessions for which the users may have already made their last
request. This is too broad a topic to really go into here, but since we
don't have a good reason to change it, we simply use Microsoft's default of 20
minutes. The next thing we do is to ensure that a session is actually
started. Until there's a reason to have a session, ASP doesn't always
bother tracking them, so it's possible that even if your browser has full cookie
support, you may not actually be assigned to a particular session on your first
hit. We avoid this by setting a session variable on initialization so that
ASP has something to keep track of and actually starts tracking our
session.
The remainder of this section is the part that
actually increments our user count. First we lock the Application
object. We need to do this because, like I said before, all our users are
sharing it and if multiple people tried to change it at the same time, it could
cause unpredictable results. Locking it gives the currently running script
exclusive access to it and makes any other script which tries to access it wait
until we unlock it. We then take the current value of our
ActiveUsers variable and increment it by one to account for the user
who is just starting their session. Finally, we unlock the Application
object so that others can access it.
Session_OnEnd
This code looks remarkable similar to the last
few lines in Session_OnStart. In fact, it's the exact same code except we
decrement the AciveUsers variable to account for the user whose session
is ending. We lock it before we do this and unlock it afterwards for the
same reason as before.
The Result
All this work basically leaves us with is one value: the
ActiveUsers application variable. It should contain, at all
times, the number of open sessions on our web server. This is not
necessarily the number of people looking at our page at that exact moment, but
it's about as close an approximation as we can get!
To access this value all you need to do is place
a line of code on your page to read the value of this variable. I've included a very simple
page which does just that below:
The only real significant piece of code here is the <%= Application("ActiveUsers") %>
which reads the value from our variable. The rest is just formatting and can easily be changed so that it
matches your site's colors and/or style.
That's It!
That's all there is to it folks. I hope you've found this article useful and I'd
very much like to hear any questions or comments you may have about it. I can be reached by
sending email to john@asp101.com. I'd also like to take
this opportunity to thank our friends from Aspin for
prompting us to write this article and for sharing their particular implementation with us.
...Well Apparently Not Quite! (added 12/17/99)
I've been getting a lot of email about this article and originally thought that it was all
just because the article was new and a lot of people were reading it... well it's around nine
months later and I'm still getting them so I'm adding this to try and explain the issue a
little.
In order for the web server to know to look for and process this global.asa file you need to
set up your directory as an application in IIS. This is done by navigating to the appropriate
web site or directory in Internet Services Manager and right clicking on it and selecting
"Properties" from the pop up menu. Then select the "Directory" or
"Home Directory" tab and on the bottom half of that window you will see the
"Application Settings" section. If it's not already, you need to make the directory
an application by pressing the "Create" button. Click "Ok" to save the
setting and exit.
In PWS on Windows 95/98 this is done differently. You need to go to the Personal Web Manager
and then click on "Advanced" in the bottom left. In the directory tree you should see
an entry named <Home>. Select it and click the "Edit Properties..." button. In
the resulting dialog box ensure that all three checkboxes are checked (Read, Execute, and
Scripts). Click "Ok" to save the setting and exit. This will enable processing of
the global.asa in PWS.
You might also find our follow-up article
Who Are Those Active Users?
interesting. It expands on this concept and actually
keeps track of information for each visitor instead of
just keeping a count of them.