Click here to Skip to main content
15,867,141 members
Articles / Web Development / Apache

A real-time visitor counter with autorefresh feature

,
Rate me:
Please Sign up or sign in to vote.
4.75/5 (9 votes)
22 Mar 2017CPOL3 min read 42.8K   462   27   8
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)


Written By
Software Developer BiTS - Softmining
Italy Italy
Luigi Di Biasi is a PhD student in a computer science @ University of Salerno.

He is sole director of Arrowsoft.IT SRLs and CIO of Softmining SRL. He is founder of dibiasi.it materiale elettrico.

He studied Computer Science at the University of Salerno in 2013. From 2014 to 2016 he is [assegnista di ricerca] for PRIN 2010-2011 Data-Centric Genomic Computing (GenData 2020)).

He collaborate to the GRIMD developing (grid for molecular dynamics), which permits the parallel and distributed computation of many kinds of scientific software, to the YADA developing (Yet Another Docking Approach), a tool that improve and refine the predictions made by VINA Autodock and to the implementation of ProtComp alignment free tools, a new algorithm for sequences analysis that outperforms existing methods. He collaborate on 7 scientific paper.

In 2017 he is responsible developer for the RIRIBOX Project (in collaboration with Acea Pinerolese and Ecofficina SRL) and for energii.dk (in collaboration with Peme IVS). All these projects has involved collaborations between Italian, Danish and American developers.

In 2016 he is responsible developer for the “Una Buona Occasione” Project (in collaboration with Ecofficina SRL). He developed all the edutainment games related to this project (16 WebGL games).

Actually, his research activities range on the AI (algorithms developing, applications) and on the game developing.

Written By
Student
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThoughts Pin
Nelek22-Mar-17 20:11
protectorNelek22-Mar-17 20:11 
SuggestionI recommend SignalR/Sockets.io for this purpose Pin
Red Feet22-Mar-17 3:28
Red Feet22-Mar-17 3:28 
GeneralMy vote of 1 Pin
Vfleitao22-Mar-17 1:49
Vfleitao22-Mar-17 1:49 
PraiseRe: My vote of 1 Pin
luigidibiasi23-Mar-17 7:15
luigidibiasi23-Mar-17 7:15 
QuestionNot Real time but just a basic ajax example Pin
Vfleitao22-Mar-17 1:48
Vfleitao22-Mar-17 1:48 
QuestionWarning Pin
Nelek22-Mar-17 1:12
protectorNelek22-Mar-17 1:12 
AnswerRe: Warning Pin
luigidibiasi23-Mar-17 7:17
luigidibiasi23-Mar-17 7:17 
GeneralRe: Warning Pin
Nelek23-Mar-17 11:03
protectorNelek23-Mar-17 11:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.