Howto Create a Hit Counter Using a Text File in ASP.NET 1.x
This article will show you how to accomplish these two tasks by storing the hit counters in static variables in the Global class and in a text file using code in the Global.asax file. This article extends the ideas from a previous article where the values of the counters were just stored in static variables. By writing the counters to a file you can maintain their values, even when you restart the Web server.
There is also a Classic ASP version of this article available.
PrerequisitesThe code in this article uses Sessions in ASP.NET, so you'll need to have them enabled on your server, by configuring the <sessionState> element in the Web.config file. Refer to the References section at the end of this article for more details.
You'll also need to have access to a file called Global.asax in the root of your site. If you run your own Web server, this is not a problem; you can simply create the file yourself. If you are using an ISP, you'll need to check with them if they support the use of the Global.asax file as, unfortunately, not all ISPs allow this.
You'll also need to be able to write to a text file from within the Global.asax file. This means that the account your Web site is running under (usually, the ASPNET account) needs sufficient permissions to write to the file.
Finally, this article assumes you're using Visual Studio .NET 2002 or 2003, as it shows code and ASPX pages using the Code Behind model. Don't worry if you don't have Visual Studio .NET; it should be relatively easy to use the code in your own Code Editor like Macromedia Dreamweaver MX or Notepad.
Counting UsersJust as in the article where the counters were stored in the Global class, you can make use of the Session_Start event, defined in the Global.asax file to keep track of your users. This event is fired for every user whenever they request the first page in your Web site. This way, you have the ability to count each unique visitor only once during their visit. As long as the Session remains active on the server, the user won't be counted again. After the Session has timed out (it will automatically time out after a certain interval when no new pages have been requested) or has been explicitly ended, a request to a page will create a new Session, Session_Start will fire and the user will be counted again.
To keep track of the total number of users that have visited your site since you started the Web server, you can increase a counter for each request a user makes. Let's call this counter totalNumberOfUsers. You can store that counter in a static variable in the Global class, so you can retrieve and display it on other pages in your site. You can also create a second counter, called currentNumberOfUsers for example, that counts the number of active Sessions on your server. Just as with totalNumberOfUsers, you increase the value of this counter whenever a new Session is started. However, you should decrease its value again when the Session ends. so you can keep track of the number of users that are currently visiting your site.
Besides storing these values in the Global class, you can also write the value of totalNumberOfUsers to a text file, so its values will survive server restarts. When your server is restarted, code in the Global.asax file will read in the old value from the text file, so your counter will continue with the value it had before the restart. Don't underestimate the problem of restarting your Web server. Even if you have your stable Windows 2003 box running for months without a reboot, the new IIS Process Recycling feature may restart your Web application every day or even every few hours.
You should start by making sure you have a file called Global.asax (note that the extension is different from ordinary aspx pages) in the root of your Web site. Usually, when you create a new Visual Studio ASP.NET Web Application, the Global.asax file is already there, and the skeleton for important events like Session_Start and Session_End are already present in its Code Behind file.
If you don't have the file, open the Visual Studio .NET Solution Explorer (Ctrl+Alt+L), right-click your Web project and choose Add | Add New Item... from the context menu. Scroll down the list with Web Project Items until you see Global Application Class. Alternatively, expand Web Project Items and then click Utility to limit the list of Web items. Select the Global Application Class and click Open to add the Global.asax file to your Web site project.
The page will open in Design View so you'll need to click the link "click here to switch to code view" to view the Code Behind file for the Global.asax file. You'll see some default using statements, followed by the definition for the Global class. I'll show you how to expand this class in the remainder of this article by adding a few private variables for the two hit counters. These private variables will then be made accessible through public properties. Using properties instead of public fields helps keeping your code cleaner and more stable. Calling code is not allowed to just arbitrarily change the field's value; it has to change the value through a public Set method. In this example, you'll make the code even a bit more safe by removing the Set method altogether. This makes it impossible for calling code to change the value of the counter; all it can do is read its value.
- Locate the code that starts with public class Global : System.Web.HttpApplication and add the following shaded lines of code:
/// Summary description for Global.
public class Global : System.Web.HttpApplication
private static int currentNumberOfUsers = 0;
/// Required designer variable.
- The code that writes the counters to a file, makes use of the StreamWriter class. This class is located in the System.IO namespace, so you'll need to add a reference to this namespace by adding a using statement, at the top of the page somewhere below the other using statements:
- The next step is to add code to the Session_Start event. This event will fire once when a user requests the first page in your Web application, so this place is perfect for your hit counter. Inside this event, the values of the two hit counters are increased; one for the total number of users and one for the current number of users. Once the values are increased, the value of totalNumberOfUsers is written to a text file.
Locate the skeleton for the Session_Start event and add the following code:
currentNumberOfUsers += 1;
string counterFile = Server.MapPath("/Counter.txt");
StreamWriter sw = File.CreateText(counterFile);
sw.WriteLine ("Total\t" + totalNumberOfUsers);
- Just as with the Session_Start event, you'll need to write some code for the Session_End event. However, instead of increasing the counters, you should decrease the counter for the current number of users only. This means that whenever a user Session times out (usually 20 minutes after they requested their last page), the counter will be decreased, so it accurately holds the number of current users on your site. You should leave the counter for the total number of users untouched.
Locate the Session_End event, and add this line of code:
How It WorksThe code that runs in the Session_Start event performs a few important steps; first of all, the values for the two hit counters are increased. Next, the code checks whether the counter file is already present. If it is, it gets deleted by calling the static Delete method of the File class. Then a new text file is created and the value of the totalNumberOfUsers is written to this new file, preceded by the word Total and a Tab character.
The code that runs in the Session_End event simply decreases the value of the current number of users. There is no need to touch the counter for the total number of users.
Making Your Counters Accessible by Other PagesSince the hit counters are stored in the Global class, you need some way to get them out of there, so you can display them on a management page for example. The easiest way to do this, is to create two public properties. Because the Global class is in many respects just an ordinary class, it is easy to add public properties to it.
To add the properties, locate the skeleton for the Application_End event, and add the following code just below it:
public static int CurrentNumberOfUsers
Reading the Counter When the Web Server StartsAs long as the Web server keeps running, this code will run fine. For each new Session that is created, the counters are increased by one and the value of totalNumberOfUsers is written to the file. If, however, the Web server stops unexpectedly, or your restart or reboot the Web server yourself, the values for totalNumberOfUsers and currentNumberOfUsers are lost. But because the value for totalNumberOfUsers has also been stored in a text file, it's easy to retrieve the counter again from that file when the Web server starts. To read in the value from the Counter.txt file, you'll need to add some code to the Application_Start event that is also defined in the Global.asax file:
string counterFile = Server.MapPath("/Counter.txt");
using (StreamReader sr = new StreamReader(counterFile))
string result = sr.ReadLine();
if (result != null)
string delimStr = "\t";
char  delimiter = delimStr.ToCharArray();
string  arrResult = null;
arrResult = result.Split(delimiter);
if (arrResult.Length == 2)
counterValue = Convert.ToInt32(arrResult);
totalNumberOfUsers = counterValue;
The string result is then split into two elements by splitting on a Tab character (\t). If you look at the code that writes to the file in the beginning of this article, you can see that the word Total, then a Tab and then the value of the counter are written to the file. Since all you're interested in is the value for the counter, you'll need to split the line in two elements; both sides of the Tab character. Because the Split method of the String class requires a char array that defines the character to split on, you'll need the additional step of converting delimStr to the array delimiter which can then be passed to the Split method of the string result.
If the Length of the array equals 2 you can be sure you read in a valid line. The second element, arrResult, is then used as the value for the local variable counterValue, by converting it to an int using Convert.ToInt32.
At the end of the code block, the static variable totalNumberOfUsers gets the value of counterValue, which holds either 0 or the value retrieved from the Counter file.
It would have been easier to leave out the TOTAL in the text file, so there is no need to split the line of text you retrieved from the file. I did include it in this example though, because it can be useful if you want to store multiple counters in the same text file. This way, you can save individual counters as TOTALUSERS, TOTALDOWNLOADS and so on all in the same file, while you can still see which value means what.
Testing it OutTo test out your hit counters, create a new Web form and call it HitCounter.aspx. You can save the form anywhere in your site. In Design View, add two labels to the page and call them lblTotalNumberOfUsers and lblCurrentNumberOfUsers respectively. Add some descriptive text before the labels, so it's easy to see what value each label will display. You should end up with something similar to this in Code View:
<form id="frmHitCounter" method="post" runat="server">
<asp:label id="lblTotalNumberOfUsers" runat="server"></asp:label><br />
Current number of users browsing the site:
<asp:label id="lblCurrentNumberOfUsers" runat="server"></asp:label><br />
int totalNumberOfUsers = HitCounters.Global.TotalNumberOfUsers;
lblCurrentNumberOfUsers.Text = currentNumberOfUsers.ToString();
lblTotalNumberOfUsers.Text = totalNumberOfUsers.ToString();
To test it out, save the page and open it in your browser. You'll see there is one current user. Also, note that the total number of users is 1. Open another browser (don't use Ctrl+N, but start a fresh instance or use an entirely different brand of browser) and open the counter page. You'll see there are two current users, and two users in total. Wait until both the Sessions have timed out (the default timeout defined in Web.config is 20 minutes) and open the hit counter page again. You'll see there is one current user, but the total number of users has been maintained and should be 3.
SummaryThis article demonstrated how to create a hit counter that keeps track of the current and total number of users to your site. It stores these counters in static variables in the Global class so they are available in each page in your Web site. It also stores the value for the total number of users in a text file so its value won't be lost when the Web server restarts.
A disadvantage of using text files is that it's difficult to scale out your Web site to lots of users. Only one user has access to the file at any given moment, so if you have a real busy site, you'll soon run into problems.
Take a look at the following article to see how you can improve the counter page even further by using a database instead of a text file.
- Howto Create a Hit Counter Using a Database in ASP.NET (http://Imar.Spaanjaars.Com/238/howto-create-a-hit-counter-using-a-database-in-aspnet-1x-with-c-sharp)
- When Sessions End - Once And For All! at www.asp101.com (http://www.asp101.com/articles/john/sessionsend/default.asp)
- Configuring ASP.NET Applications to Use the Appropriate Session State (http://technet2.microsoft.com/WindowsServer/en/Library/51aa77d2-4485-4cb9-a75f-9186dc5d775f1033.mspx)
- <sessionState> Element (http://msdn.microsoft.com/library/en-us/cpgenref/html/gngrfsessionstatesection.asp)
- Source Code for this Article (http://Imar.Spaanjaars.Com/Downloads/Articles/HitCounterInTextFileInASP.NET.zip)
Where to Next?
Wonder where to go next? You can post a comment on this article.
Links in this Document
Talk Back! Comment on Imar.Spaanjaars.Com
I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.
When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.
If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the Doc ID of the document.
Please correct the following errors:
Unfortunately, something went wrong and your message or comments have not been submitted successfully.
There's a fair chance things broke down because you tried to post something that looks like HTML. Things that look like HTML include (X)HTML, obviously, XML, ASP.NET markup and c# generics syntax as all of them use the < and > characters.
If that's the case, try altering your message and remove anything that looks like an angled bracket. You can replace them with [ and ] for example so you can still make it look like HTML to some extend.
If, on the other hand, you were trying to spam this web site, I am pretty glad I caught you in the act and stopped you from doing so ;-)
Also, please don't use links in your posts; I had to block them to filter out most of the junk mail I am receiving.