65.9K
CodeProject 正在变化。 阅读更多。
Home

带有自动刷新功能的实时访客计数器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (9投票s)

2013年12月26日

CPOL

3分钟阅读

viewsIcon

45359

downloadIcon

546

使用 AJAX 和服务器端语言构建一个实时访客计数器。

引言

您好!这是我在 CodeProject 上的第一篇文章,所以希望您能原谅我拙劣的英语以及在使用模板时的一些错误。我想介绍如何结合 AJAX 和任何服务器端语言来开发一个带有自动刷新功能的简单实时访客计数器。  

我们(以及我)的目标是构建一个组件(访客计数器,放置在一个或多个网页中),它将在用户不采取任何操作的情况下自动刷新。

可以使用您喜欢的服务器端技术,如 ASP.NET 或 PHP,但本文我将使用 Javascript + ASP.NET:我们只需要服务器端的 ASPX 页面和客户端的一些函数。请记住,这个概念可以在任何您想要的语言中实现。 

为了阅读本文,您应该(不一定精通)了解:JavaScript、AJAX、ASP.NET,以及 ASP.NET 应用程序、回调函数和 UUID 的含义。  

思路如下:我们需要在服务器端维护一个键值对为 <唯一 ID, 日期> 的哈希字典,以便了解每个用户的最后活动时间;当 当前时间 - 最后活动时间 > 30 秒 时,我们将认为用户“已过期”。显然,我们需要一种从客户端执行“时间戳”刷新的方法,以避免移除活动用户。 

在服务器端,我们只需要:    

  • 一个 Dictionary<string,date>   
  • 一个 ASPX 页面 

服务器端的伪代码如下: 

OnApplicationStart:
   ActiveUser = {}
 
OnPing(UUID):
   if not UUID in ActiveUser
      add (UUID,Current_time) in ActiveUser
   else
      update UUID in ActiveUser with Current_Time 

在客户端,我们需要执行一个简单的操作:当用户首次访问我们的页面时,我们必须为其分配一个唯一 ID。该 ID 将存储在 cookie 中。对于后续的每次访问,我们只需要读取 cookie 并将其发送到服务器。此外,我们需要一个显示活动用户数量的地方,以及一个用于查询服务器以检索此数量的函数。因此,在客户端,我们需要: 

  • 一个 <span> 标签  :) 
  • 一个 <javascript> 块  
  • 一个允许我们生成唯一 ID 的方法(我将在本文中提供一个简易方法) 

客户端的伪代码如下

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

使用代码

现在我们可以开始编码了:我们创建一个名为 serverside.aspx 的新页面,其内容如下:  

<%@ 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
  
%>

同样,创建一个名为 clientside.aspx 的新 ASPX 页面,其内容如下:  

<%@ 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>

关注点

我们应该注意几点:    

  • 为了测试代码,您可以在 VS2010 中以 Debug 模式运行项目,并启动 clientside.aspx:计数器将显示 1。随后,您可以使用 Chrome 浏览器的“无痕模式”打开更多窗口,您将看到访问者数量增加。  如果您停止打开新窗口,您将看到访问者数量减少,因为 ping 仅在新页面打开时触发。(您可以更改此行为。)  
  • ASP.NET 回收可能会清除 ActiveUser 变量(但这不成问题,因为在下一次点击时用户将被重新计数)   
  • 文件 utils.js 包含 getCookiesetCookiegetXMLReq 函数(以便代码更简洁)。   
  • 服务器端代码可以用 PHP 实现,并且可以在与客户端不同的网站上运行。  
  • 本文附带的项目是用 VS2010 Express 编写的,但可以在任何与 .NET 2.0 兼容的 IDE 中执行。   
  • UUID 生成器非常、非常简陋。在高负载环境下可能会出现一些冲突。  
  • 请注意:在跨域使用此代码之前,需要启用 Ajax 跨域调用。  

历史  

  • 2013/12/26:首次发布。 
  • 2014/01/01:拼写错误修正 
  • 2014/01/02:拼写错误修正和变量重命名。  
  • 2014/01/13:添加了 Ajax 跨域信息。 
  • 2014/01/14:添加了之前忘记的 zip 文件 
  •  2014/01/15:由 Bruno Interlandi 进行英语修订 
© . All rights reserved.