how to show the current number of online visitors for ASP.NET website? we demonstrated that its really simple to count the number of people that are currently browsing your website.
If you have added this option to your website, at some point of time, you have undoubtedly asked yourself:
"who are those 17 people that are currently browsing my precious website ?"
"What are their IP addresses? when did they come here, what page they first opened and from where did they came from?"
Yes i know you can see that information later by examining the WebSitelogs, but having the current information is something that no laterexamination of the logs can compensate.
The trouble is that there is no standard way to enumerate active visitor sessions on your ASP.NET website.
You can only enumerate keys and values of the current HTTPContext Session for each user but this is not enough.
This is caused by the builtin ASP.NET security measure that forbids individual user sessions to access other sessions.
(If this would not be forbidden, many bad things could happen to you while you browse a website, so this is a good thing).
But if there is no easy way to get the list of all current user sessions, that does not mean we cannot track them.
Here is what we are going to do:
We will create a global collection that will hold the list of allactive user sessions, and as each new user hits our website we will addhis/hers information to this list, also when any of the sessionexpires, we will remove its information from this global list.
First we must setup our WebSite session configuration in web.config like this:
<sessionState mode="InProc" cookieless="false" timeout="30"/>
This will tell the ASP.NET runtime to create a session in the memory ofthe Web Server for each visitor and keep it alive for 30 minutes.
If user keeps browsing our website and opens some of the pages every few minutes, his session is kept alive.
But if he does not open any new pages on our website for 30 or more minutes, his session will expire.
Now how do we know when a new session is created or when it expires?
This is why ASP.NET is great: you can add to your website Global.asax class (file) where you can easily add code to handlers for some very important application-level events.
(To create a new Global.asax class file in your ASP.NET website,right-click on the website in the Solution Explorer, and choose options:
ADD NEW ITEM->GLOBAL APPLICATION CLASS).
There are number of application events that you can handle in thisclass, but in this case, events that are important for us are Session_Start and Session_End.
Session_Start event handler code is fired when new visitor comes to ourwebsite, and Session_End is fired for each visitor if he does not openany pages on our website longer than our TIMEOUT setting insessionState configuration of web.config file.
NOTE: ASP.Net runtime hasno other way of knowing if user has left our website. If user opens onepage, and then immediately closes his web-browser, this will nottrigger the Session_End event at once. This event will eventuallytrigger when the Timeout timer elapses.
Now that we know how to hook into the process of creating anddestroying visitor sessions, we need to define a simple WebsiteVisitorobject that we will create for each visitor to store information abouthim and keep it in our global collection.
Here is the definition of WebsiteVisitor class:
namespace Roboblob.Utility
{
public class WebsiteVisitor
{
private string sessionId;
public string SessionId
{
get { return sessionId; }
set { sessionId = value; }
}
public string IpAddress
{
get { return ipAddress; }
set { ipAddress = value; }
}
private string ipAddress;
public string UrlReferrer
{
get { return urlReferrer; }
set { urlReferrer = value; }
}
private string urlReferrer;
public string EnterUrl
{
get { return enterUrl; }
set { enterUrl = value; }
}
private string enterUrl;
public string UserAgent
{
get { return userAgent; }
set { userAgent = value; }
}
private string userAgent;
public DateTime SessionStarted
{
get { return sessionStarted; }
set { sessionStarted = value; }
}
private DateTime sessionStarted;
public WebsiteVisitor(HttpContext context)
{
if ((context != null) && (context.Request != null) && (context.Session != null) )
{
this.sessionId = context.Session.SessionID;
sessionStarted = DateTime.Now;
userAgent = string.IsNullOrEmpty(context.Request.UserAgent) ? "" : context.Request.UserAgent;
ipAddress = context.Request.UserHostAddress;
if (context.Request.UrlReferrer != null)
{
urlReferrer = string.IsNullOrEmpty(context.Request.UrlReferrer.OriginalString) ? "" : context.Request.UrlReferrer.OriginalString;
}
enterUrl = string.IsNullOrEmpty(context.Request.Url.OriginalString) ? "" : context.Request.Url.OriginalString;
}
}
}
}
}
In the constructor of the object we pass current HttpContext object asthe parameter that will allow our object to extract all the data needed.
In this example we track the visitors IP Address, SessionID, BrowserUser Agent string, Date and Time when user entered our website, FirstPage he opened, and Referrer Url from which he visited us (If any).
Here you can add some other interesting data if you can retrieve it. Its up to you.
Now let us create a global collection where we will keep our active WebsiteVisitor objects:
To make it available to all of our pages, we will put it in publicstatic class in the same file where we placed WebsiteVisitor object:
public static class OnlineVisitorsContainer
{
public static Dictionary<string, WebsiteVisitor> Visitors = new Dictionary<string,WebsiteVisitor>();
}
As you can see its a generic Dictionary class, where keys (that areused to store and retrieve values from the dictionary) are of string type, while the values are of WebsiteVisitor type.
We have declared our container class OnlineVisitorsContainer and Dictionary collection Visitors as static.
This way the container class cannot be instantiated, and therecan be only one instance of Visitors collection to be shared amongstall of our pages and code.
Now you are probably thinking: what we will use as a string key for each Visitor?
Well, this is a tricky question.
The most logical answer is to use the SessionID that is anyway createdfor each visitor as unique identifier for his session data.
Now that we solved this dilemma, lets us move to the code in the event handlers in Global.asax file:
First add these lines to top of your Global.asax file to import namespaces that we will use:
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="Roboblob.Utility" %>
Here is the code for the application event that gets fired when sesion is started (when visitor comes to our website):
void Session_Start(object sender, EventArgs e)
{
// add some data to the Session so permanent SessionId is assigned
// otherwise new SessionId is assigned to the user until some data
// is actually written to Session
Session["Start"] = DateTime.Now;
// get current context
HttpContext currentContext = HttpContext.Current;
if (currentContext != null)
{
if (!OnlineVisitorsContainer.Visitors.ContainsKey(currentContext.Session.SessionID))
{
WebsiteVisitor currentVisitor = new WebsiteVisitor(currentContext);
OnlineVisitorsContainer.Visitors.Add(currentVisitor.SessionId, currentVisitor);
}
}
}
So in the event handler that is fired when session is created, we firstplace current Date and Time in the current session so that permanentSessionID is assigned to the Session.
If we do not do that, it can happen that new SessionID is created forthe same user again and again until something is actually written tothe Session collection.
Afterwards, we get the reference to current HttpContext and pass it to the constructor of the new WebsiteVisitor object and add this object to the global Visitors collection.
Now to the Session_End event handler code:
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
if (this.Session != null)
OnlineVisitorsContainer.Visitors.Remove(this.Session.SessionID);
}
In Session_End event handler we cannot access current HttpContextinstance, but we can access current Session and therefore we canretrieve current SessionID and use it to remove current visitorssession from the our global collection of online visitors.
We have finished most of our work. We have implemented all thenecessary code that will keep our global collection updated, and itwill always hold the list of the visitors that are currently browsingour website.
Lets add GridView and some code to Default.aspx page that will show our active website visitors:
<asp:GridView ID="gvVisitors" runat="server" AutoGenerateColumns="False"
CellPadding="2" ForeColor="#333333" GridLines="Both" >
<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#EFF3FB" />
<Columns>
<asp:TemplateField HeaderText="Session Started">
<ItemTemplate>
<%# ((DateTime)Eval("SessionStarted")).ToString("dd/MM/yyyy HH:mm:ss") %><br />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Ip">
<ItemTemplate>
<%# Eval("IpAddress") %>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Other">
<ItemTemplate>
<span style="font-size:small;">
<%# Eval("UserAgent") %><br />
<%# Eval("EnterUrl") %><br />
</span>
<asp:HyperLink ID="refurl" Text='<%# Eval("UrlReferrer") %>' Font-Size="Small"
NavigateUrl='<%# Eval("UrlReferrer") %>' runat="server" Target="_blank" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<EditRowStyle BackColor="#2461BF" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>
And now we must only bind our global visitors collection from OnlineVisitorsContainer class to the GridVIew control:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (OnlineVisitorsContainer.Visitors != null)
{
gvVisitors.DataSource = OnlineVisitorsContainer.Visitors.Values;
gvVisitors.DataBind();
}
}
}
If you dont click on any pages for 30 minutes in one of the browsers it will eventually disappear from the list.
here is how it should look: