Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Apache

A real-time visitor counter with autorefresh feature

4.75/5 (9 votes)
22 Mar 2017CPOL3 min read 44.5K   463  
Build a realtime visitors counter with AJAX and a server-side language.

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: 

JavaScript
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:

JavaScript
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:  

VB
<%@ Page Language="vb" AutoEventWireup="false" 
   CodeBehind="serverside.aspx.vb" Inherits="RealTimeCouter.serverside" %>

<%

    ' we make sure that counter 'memory' is available
    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
    
    ' on PING receive we check if UUID is known
    ' then save last action date and time
    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
        
    'on QUERY receive we return the number of UUID with 
    'last action timestamp value <30 sec from current timestamp
    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:  

ASP.NET
<%@ 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">

    // Check for UUID of this user
    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);
    }
    // send uuid to server (the ping)
    var ping = getXMLReq();
    ping.open("GET", "./serverside.aspx?TYPE=PING&UUID=" + uuid, true);
    ping.send();

    // Refresh number of visitors each 5 seconds
    setInterval('loadXMLDoc()', 5000);

    // Refresh number of visitor at page load
    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 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)