Introduction
Hi there! This is my first article on CodeProject, so I hope you forgive me for my poor English and for some mistakes using the template. I would like to describe how to use AJAX combined with any server-side languages to develop a simple real-time visitors counter with auto-refresh feature.
My (and our) goal will be to build a component (the visitors counter, placed in one or more web pages) that will automatically be refreshed without user action.
It is possible to use your preferred server-side technology like ASP.NET or PHP but, for this article, i will use Javascript + ASP.NET: we only need one ASPX page on server side and some functions on client side. Keep in mind that the concept can be implemented in any language you want.
For the purpose of this article you should know (not deeply): JavaScript, AJAX, ASP.NET, and the meaning of ASP.NET Application, callback function, and UUID.
The idea is as follows: we need to maintain a hash dictionary of pairs <unique_id,date> on server side in order to know the last action time for each user; we will consider a user 'expired' when the difference between current_time - last_action_time > 30 secs. Obviously, we need a way to perform 'time_stamp' refresh from the client side in order to avoid removing of an active user.
On server-side we only need:
- A
Dictionary<string,date>
- An ASPX page
The pseudo code for the server side is as follows:
OnApplicationStart:
ActiveUser = {}
OnPing(UUID):
if not UUID in ActiveUser
add (UUID,Current_time) in ActiveUser
else
update UUID in ActiveUser with Current_Time
On client-side, we need to perform a simple action: the first time a user enter one of our pages we must assign it a unique ID. The ID will be stored in a cookie. For each following access we only need to read the cookie and send it to the server. Furthermore, we need a place where the number of active users will be shown and a function for querying the server in order to retrieve this number. So, in the client-side, we need:
- A
<span>
tag :) - A
<javascript>
block - A method that allow us to generate unique IDs (I will provide a lazy method in this post)
The pseudo code for the client side is as follows:
PageLoad:
UUID = getCookie("MySite_UUID")
if is null UUID
UUID = generate_uuid()
setCookie("MySite_UUID",UUID)
Ping_Server(UUID)
Every_5_seconds:
call_server_for_num()
Server_Callback(server_data)
document.getElementyById("span1").value= server_data.nuser
Using the code
Now we can start coding: We create a new page named serverside.aspx with the following content:
<%@ Page Language="vb" AutoEventWireup="false"
CodeBehind="serverside.aspx.vb" Inherits="RealTimeCouter.serverside" %>
<%
SyncLock Application
Dim ActiveUser As Dictionary(Of String, Date)
ActiveUser = CType(Application("ActiveUser"), Dictionary(Of String, Date))
If IsNothing(ActiveUser) Then
ActiveUser = New Dictionary(Of String, Date)
Application.Add("ActiveUser", ActiveUser)
End If
Application.Add("ActiveUser", ActiveUser)
End SyncLock
If Request("TYPE") = "PING" Then
Dim UUID As String = Request("UUID")
SyncLock CType(Application("ActiveUser"), Dictionary(Of String, Date))
If Not CType(Application("ActiveUser"), Dictionary(Of String, Date)).ContainsKey(UUID) Then
CType(Application("ActiveUser"), Dictionary(Of String, Date)).Add(UUID, Date.Now)
Else
CType(Application("ActiveUser"), Dictionary(Of String, Date))(UUID) = Date.Now
End If
End SyncLock
ElseIf Request("TYPE") = "QUERY" Then
Dim nusr As Integer = 0
For Each it As KeyValuePair(Of String, Date) In _
CType(Application("ActiveUser"), Dictionary(Of String, Date))
If Math.Abs(DateDiff(DateInterval.Second, it.Value, Date.Now)) <= 30 Then
nusr += 1
End If
Next it
Response.Write(nusr)
Return
End If
%>
Again, create a new ASPX page named clientside.aspx with the following content:
<%@ Page Language="vb" AutoEventWireup="false"
CodeBehind="clientside.aspx.vb" Inherits="RealTimeCouter.clientside" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript"
language="javascript" src="./utils.js"></script>
</head>
<body>
<div>
<span id="nvis">0 </span> visitors
</div>
<script language="javascript" defer="defer">
var uuid = getCookie("site_uuid");
if (uuid == "") {
var d = new Date();
var rnd = Math.floor((Math.random() * 100000) + 1);
uuid = rnd + '_' + d.getSeconds() + '_' + d.getMilliseconds() + '_' + d.getMinutes() + '_' + d.getHours();
setCookie("site_uuid", uuid);
}
var ping = getXMLReq();
ping.open("GET", "./serverside.aspx?TYPE=PING&UUID=" + uuid, true);
ping.send();
setInterval('loadXMLDoc()', 5000);
loadXMLDoc();
function loadXMLDoc() {
var xmlhttp = getXMLReq();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("nvis").innerText = xmlhttp.responseText;
}
}
xmlhttp.open("GET", "./serverside.aspx?TYPE=QUERY", true);
xmlhttp.send();
xmlhttp = none;
}
</script>
</body>
</html>
Points of Interest
We should note a few things:
- In order to test the code you can run the project in Debug mode with VS2010 and start clientside.aspx: the counter will show 1. Subsequently, you can open more 'incognito' windows with Chrome browser so you can see the number of visitors increasing. If you stop opening new windows, you can view the number of visitors decreasing because the ping is launched only when a new page is opened. (You can change this behavior.)
- ASP.NET recycling can clear the
ActiveUser
variable (but this isn't a problem because at the next click the users will be counted again) - The file utils.js contains
getCookie
, setCookie
, and getXMLReq
functions (in order to make the code clean). - The server-side code could be implemented in PHP and could be run in a website different to the one of the clientside.
- The project attached with this article is written with VS2010 Express but is executable in any IDE compatible with .NET 2.0.
- The UUID generator is very, very lazy. some collision can appear in a high load environment.
- Keep in mind: Ajax cross domain call will be enabled before using this code between different domains.
History
- 26/12/2013: First release.
- 01/01/2014: Misspelling correction
- 02/01/2014: Misspelling correction and variables renaming.
- 13/01/2014: Ajax cross domain info added.
- 14/01/2014: Added zip that i had forgotten
- 15/01/2014: English revision made by Bruno Interlandi