一个在线用户统计程序源码

来源:互联网 发布:yii2advanced 数据库 编辑:程序博客网 时间:2024/05/02 01:42

#define DEBUG // 用于调试

/*
 * OnlineUserService.cs @Microsoft Visual Studio 2005 <.NET Framework 2.0>
 * AfritXia
 * 11.21/2005
 *
 * 在线用户列表服务
 *
 * 实现原理是在缓存中建立两张数据表:OnlineUserTb 数据表和 SessionInfoTb 数据表
 *
 * 这两个表的表格结构分别是:
 *
 * +-------------------------------------+
 * | OnlineUserTb                        |
 * +----+---------------+----------------+
 * | PK | NickName      | System.String  |
 * |    +---------------+----------------+
 * | PK | CurrSessionID | System.String  |
 * |    +---------------+----------------+
 * |    | ActiveTime    | System.Int64   |
 * |    +---------------+----------------+
 * |    | RequestURL    | System.String  |
 * +----+---------------+----------------+
 *
 * +-----------------------------------+
 * | SessionInfoTb                     |
 * +----+-------------+----------------+
 * | PK | SessionID   | System.String  |
 * |    +-------------+----------------+
 * |    | NickName    | System.String  |
 * |    +-------------+----------------+
 * |    | ActiveTime  | System.Int64   |
 * |    +-------------+----------------+
 * |    | RequestURL  | System.String  |
 * +----+-------------+----------------+
 *
 * SessionInfoTb 数据表的主要目的是用来维护 OnlineUserTb 数据表
 *
 * OnlineUserTb 用来对外提供数据
 *
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Threading;
using System.Web;

using Bincess.Classes.Users;

namespace Bincess.WebForum
{
 /// <summary>
 /// OnlineUserService 在线用户列表服务
 /// </summary>
 public class OnlineUserService
 {
  // OnlineUserService 单列对象
  private static OnlineUserService g_theInstance = null;

  // 在线用户信息集合
  private DataSet m_onlineUserDS = null;

  // OnlineUser 数据表
  private const string TB_ONLINE_USER = "OnlineUser";
  // SessionInfo 数据表
  private const string TB_SESSION_INFO = "SessionInfo";
  // 用户昵称
  private const string COL_NICK_NAME = "NickName";
  // 当前 SessionID
  private const string COL_CURR_SESSION_ID = "CurrSessionID";
  // 用户最后活动时间
  private const string COL_ACTIVE_TIME = "ActiveTime";
  // 用户最后活动页 URL
  private const string COL_REQUEST_URL = "RequestURL";
  // Session 编号
  private const string COL_SESSION_ID = "SessionID";

  // 访问者数量
  private int m_visitorCount = 0;
  // 超时分钟设置
  private int m_timeOutMinutes = 20;
  // 工作模式
  private WorkMode m_inWorkMode = WorkMode.MultiThreading;

  #region 类 OnlineUserService 构造器
  /// <summary>
  /// 类 OnlineUserService 默认构造器
  /// </summary>
  private OnlineUserService()
  {
  }
  #endregion

  /// <summary>
  /// 获取 OnlineUserService 类实例
  /// </summary>
  public static OnlineUserService Instance
  {
   get
   {
    if (g_theInstance != null)
     return g_theInstance;

    lock (typeof(OnlineUserService))
    {
     if (g_theInstance == null)
     {
      OnlineUserService theInstance = new OnlineUserService();

      // 初始化在线用户数据表
      theInstance.InitOnlineUserDS();

      g_theInstance = theInstance;
     }
    }

    return g_theInstance;
   }
  }

  #region InitOnlineUserDS 初始化在线用户数据集
  /// <summary>
  /// 初始化在线用户数据集
  /// </summary>
  private void InitOnlineUserDS()
  {
   this.m_onlineUserDS = new DataSet("OnlineUserDS");

   // 在线用户数据表
   DataTable onlineUserTb = new DataTable(TB_ONLINE_USER);

   // 为 OnlineUser 数据表添加列
   // 添加数据列 NickName
   onlineUserTb.Columns.Add(new DataColumn(COL_NICK_NAME, typeof(System.String)));
   // 添加数据列 CurrSessionID
   onlineUserTb.Columns.Add(new DataColumn(COL_CURR_SESSION_ID, typeof(System.String)));
   // 添加数据列 ActiveTime
   onlineUserTb.Columns.Add(new DataColumn(COL_ACTIVE_TIME, typeof(System.Int64)));
   // 添加数据列 RequestURL
   onlineUserTb.Columns.Add(new DataColumn(COL_REQUEST_URL, typeof(System.String)));

   // 设置数据列 CurrSessionID 为非重复的列
   onlineUserTb.Columns[COL_CURR_SESSION_ID].Unique = true;

   // 设置主键列为 NickName, CurrSessionID
   onlineUserTb.PrimaryKey = new DataColumn[] {
    onlineUserTb.Columns[COL_NICK_NAME],
    onlineUserTb.Columns[COL_CURR_SESSION_ID]
   };

   // 将数据表添加到在线用户信息集合
   this.m_onlineUserDS.Tables.Add(onlineUserTb);

   // Session 信息数据表
   DataTable sessionInfoTb = new DataTable(TB_SESSION_INFO);

   // 为 SessionInfo 数据表添加列
   // 添加数据列 SessionID
   sessionInfoTb.Columns.Add(new DataColumn(COL_SESSION_ID, typeof(System.String)));
   // 添加数据列 NickName
   sessionInfoTb.Columns.Add(new DataColumn(COL_NICK_NAME, typeof(System.String)));
   // 添加数据列 ActiveTime
   sessionInfoTb.Columns.Add(new DataColumn(COL_ACTIVE_TIME, typeof(System.Int64)));
   // 添加数据列 RequestURL
   sessionInfoTb.Columns.Add(new DataColumn(COL_REQUEST_URL, typeof(System.String)));

   // 设置主键为 SessionID
   sessionInfoTb.PrimaryKey = new DataColumn[] {
    sessionInfoTb.Columns[COL_SESSION_ID]
   };

   // 将数据表添加到在线用户信息集合
   this.m_onlineUserDS.Tables.Add(sessionInfoTb);

   // 为 SessionInfoTb 数据表添加事件
   sessionInfoTb.RowDeleted += new DataRowChangeEventHandler(SessionInfoTb_RowDeleted);
   sessionInfoTb.RowChanged += new DataRowChangeEventHandler(SessionInfoTb_RowChanged);
  }

  /// <summary>
  /// SessionInfoTb 数据表修改事件
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void SessionInfoTb_RowChanged(object sender, DataRowChangeEventArgs e)
  {
   if (e.Row.RowState != DataRowState.Added && e.Row.RowState != DataRowState.Modified)
    return;

   // 获取 SessionID
   string sessionID = e.Row[COL_SESSION_ID] as string;
   // 获取用户昵称
   string nickName = e.Row[COL_NICK_NAME] as string;
   // 获取最后活动时间
   long activeTimeTicks = (long)e.Row[COL_ACTIVE_TIME];
   // 获取最后请求地址
   string requestURL = e.Row[COL_REQUEST_URL] as string;

   // 获取 OnlineUserTb 数据表
   DataTable onlineUserTb = this.m_onlineUserDS.Tables[TB_ONLINE_USER];

   // 获取满足条件的数据行
   // ( nickName != "" && COL_NICK_NAME == nickName ) || COL_CURR_SESSION_ID == sessionID
   DataRow[] rows = onlineUserTb.Select(
    String.Format("( {0} = '{1}' AND '{1}' <> '' ) OR {2} = '{3}'",
    COL_NICK_NAME, nickName, COL_CURR_SESSION_ID, sessionID));

   if (rows == null || rows.Length <= 0)
   {
    DataRow row = onlineUserTb.NewRow();

    // 设置在线用户昵称
    row[COL_NICK_NAME] = nickName;
    // 当前 SessionID
    row[COL_CURR_SESSION_ID] = sessionID;
    // 最后活动时间
    row[COL_ACTIVE_TIME] = activeTimeTicks;
    // 最后的请求地址
    row[COL_REQUEST_URL] = requestURL;

    lock (onlineUserTb)
    {
     // 添加并保存新的数据行
     onlineUserTb.Rows.Add(row);
     onlineUserTb.AcceptChanges();

     // 访问者数量加 1
     this.m_visitorCount += 1;
    }
   }
   else
   {
    if (rows.Length >= 2)
    {
     for (int i = rows.Length - 1; i > 0; i--)
      rows[i].Delete();

     // 访问者数量减 1
     this.m_visitorCount -= rows.Length - 1;
    }

    lock (rows[0])
    {
     // 更新在线用户昵称
     rows[0][COL_NICK_NAME] = nickName;
     // 当前 SessionID
     rows[0][COL_CURR_SESSION_ID] = sessionID;
     // 最后活动时间
     rows[0][COL_ACTIVE_TIME] = activeTimeTicks;
     // 最后的请求地址
     rows[0][COL_REQUEST_URL] = requestURL;

     // 保存修改
     rows[0].AcceptChanges();
    }
   }
  }

  /// <summary>
  /// SessionInfoTb 数据表删除事件
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void SessionInfoTb_RowDeleted(object sender, DataRowChangeEventArgs e)
  {
   if (e.Row.RowState != DataRowState.Deleted)
    return;

   // 获取 SessionID
   string sessionID = e.Row[COL_SESSION_ID, DataRowVersion.Original] as string;
   // 获取用户昵称
   string nickName = e.Row[COL_NICK_NAME, DataRowVersion.Original] as string;

   // 获取在线用户信息数据表
   DataTable onlineUserTb = this.m_onlineUserDS.Tables[TB_ONLINE_USER];

   lock (onlineUserTb)
   {
    DataRow[] rows = onlineUserTb.Select(
     String.Format("( {0} = '{1}' AND '{1}' <> '' ) OR {2} = '{3}'",
     COL_NICK_NAME, nickName, COL_CURR_SESSION_ID, sessionID));

    // 删除数据行
    foreach (DataRow r in rows)
     r.Delete();

    // 减少访问者数量
    this.m_visitorCount -= rows.Length;
   }
  }
  #endregion

#if DEBUG
  /*
   * 在 DEBUG 标识被开启时,OnlineUserTb 和 SessionInfoTb 数据表可以被外界所访问。
   *
   * 而对外开放 OnlineUserTb 和 SessionInfoTb 这两个数据表的主要目的是用于跟踪和调试此程序文件。
   *
   * 所以,在使用正式版的程序时,请关闭 DEBUG 标志
   *
   * 若获取在线用户信息,请使用 GetOnlineUsers 函数
   *
   */

  /// <summary>
  /// 获取 OnlineUserTb 数据表
  /// </summary>
  public DataTable OnlineUserTb
  {
   get
   {
    return this.m_onlineUserDS.Tables[TB_ONLINE_USER];
   }
  }

  /// <summary>
  /// 获取 SessionInfoTb 数据表
  /// </summary>
  public DataTable SessionInfoTb
  {
   get
   {
    return this.m_onlineUserDS.Tables[TB_SESSION_INFO];
   }
  }
#endif

  /// <summary>
  /// 设置或获取超时分钟数,最小值为 1 分钟
  /// </summary>
  public int TimeOutMinutes
  {
   set
   {
    this.m_timeOutMinutes = (value <= 0 ? 1 : value);
   }

   get
   {
    return this.m_timeOutMinutes;
   }
  }

  /// <summary>
  /// 设置或获取工作状态
  /// </summary>
  public WorkMode InWorkMode
  {
   set
   {
    this.m_inWorkMode = value;
   }

   get
   {
    return this.m_inWorkMode;
   }
  }

  /// <summary>
  /// 保存在线用户信息
  /// </summary>
  /// <param name="sessionID">SessionID</param>
  /// <param name="onlineUser">在线用户信息</param>
  public void Persist(string sessionID, OnlineUser onlineUser)
  {
   if (this.m_inWorkMode == WorkMode.MultiThreading)
   {
    // 建立保存工作线程对象
    PersistWorkThread workThread = new PersistWorkThread(sessionID, onlineUser);

    Thread t = new Thread(new ThreadStart(workThread.WorkStart));
    t.Start();
   }
   else
   {
    // 建立保存动作的代理对象
    MyDelegatePersist @delegate = new MyDelegatePersist(this.Async_Persist);

    // 启用异步调用
    @delegate.BeginInvoke(sessionID, onlineUser, null, null);
   }
  }

  /// <summary>
  /// 保存在线用户信息,异步调用
  /// </summary>
  /// <param name="sessionID">SessionID</param>
  /// <param name="onlineUser">在线用户信息</param>
  private void Async_Persist(string sessionID, OnlineUser onlineUser)
  {
   if (sessionID == null || sessionID == "" || onlineUser == null)
    return;

   // 获取 SessionInfoTb 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   if (!this.HasSessionIDInSessionInfoTb(sessionID))
   {
    // 添加在线用户信息
    this.Append(sessionID, onlineUser);
   }
   else
   {
    // 更新在线用户信息
    this.Update(sessionID, onlineUser);
   }

   // 删除超时在线用户信息
   this.DeleteTimeOut();
  }

  /// <summary>
  /// 保存在线用户信息
  /// </summary>
  /// <param name="sessionID">SessionID</param>
  /// <param name="onlineUser">在线用户信息</param>
  private void Append(string sessionID, OnlineUser onlineUser)
  {
   if (sessionID == null || sessionID == "" || onlineUser == null)
    return;

   // 获取 SessionInfo 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   // 建立新数据行
   DataRow row = sessionInfoTb.NewRow();

   // 设置 SessionID
   row[COL_SESSION_ID] = sessionID;
   // 用户昵称
   row[COL_NICK_NAME] = (onlineUser.NickName == null ? "" : onlineUser.NickName);
   // 最后活动时间
   row[COL_ACTIVE_TIME] = onlineUser.ActiveTime.Ticks;
   // 最后请求地址
   row[COL_REQUEST_URL] = onlineUser.RequestURL;

   lock (sessionInfoTb)
   {
    /*
     * if (this.InWorkMode == WorkMode.MultiThreading)
     *     Thread.Sleep(24);
     *
     * 当工作模式为 MultiThreading 多线程模式时,可以令当前线程先休眠……
     *
     * 让 CPU 可以空闲出来处理其他肭螅纯梢蕴岣咔肭笏俣取?
     *
     * 但要特别注意的是,线程陷入过长时间的休眠,会造成程序的不稳定,也会增加内存的使用率。
     *
     * 最终 .NET 可能会警告 System.OutOfMemoryException 异常!
     *
     * 所以,休眠时间应当根据具体硬件环境进行微调……
     *
     */

    // 添加数据行并保存修改
    sessionInfoTb.Rows.Add(row);
    sessionInfoTb.AcceptChanges();
   }
  }

  /// <summary>
  /// 删除超时在线用户信息
  /// </summary>
  private void DeleteTimeOut()
  {
   // 获取 SessionInfoTb 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   lock (sessionInfoTb)
   {
    // 获取 SessionInfoTb 数据表中的超时行
    DataRow[] rows = sessionInfoTb.Select(
     String.Format("{0} <= {1}", COL_ACTIVE_TIME, this.TimeOutLimit));

    // 删除数据行
    foreach (DataRow r in rows)
     r.Delete();
   }
  }

  /// <summary>
  /// 更新在线用户信息
  /// </summary>
  /// <param name="sessionID">SessionID</param>
  /// <param name="onlineUser">在线用户信息</param>
  private void Update(string sessionID, OnlineUser onlineUser)
  {
   if (sessionID == null || onlineUser == null)
    return;

   // 获取 SessionInfoTb 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   DataRow[] rows = sessionInfoTb.Select(String.Format("{0} >= {1} AND {2} = '{3}'",
    COL_ACTIVE_TIME, this.TimeOutLimit, COL_SESSION_ID, sessionID));

   if (rows == null || rows.Length <= 0)
    return;

   lock (rows[0])
   {
    /*
     * if (this.InWorkMode == WorkMode.MultiThreading)
     *     Thread.Sleep(24);
     *
     */

    // 设置用户昵称
    rows[0][COL_NICK_NAME] = (onlineUser.NickName == null ? "" : onlineUser.NickName);
    // 最后活动时间
    rows[0][COL_ACTIVE_TIME] = DateTime.Now.Ticks;
    // 最后活动页
    rows[0][COL_REQUEST_URL] = onlineUser.RequestURL;

    // 保存修改
    rows[0].AcceptChanges();
   }
  }

  /// <summary>
  /// 判断在 OnlineUserTb 数据表中是否存在指定的用户昵称
  /// </summary>
  /// <param name="nickName">用户昵称</param>
  /// <returns></returns>
  private bool HasNickNameInOnlineUserTb(string nickName)
  {
   if (nickName == null)
    return false;

   // 获取在线用户数据表
   DataTable onlineUserTb = this.m_onlineUserDS.Tables[TB_ONLINE_USER];

   // 统计用户昵称数量
   object count = onlineUserTb.Compute(
    String.Format("COUNT({0})", COL_NICK_NAME),
    String.Format("{0} = '{1}'", COL_NICK_NAME, nickName));

   return Convert.ToInt32(count) >= 1;
  }

  /// <summary>
  /// 判断在 OnlineUserTb 数据表中是否存在指定的 CurrSessionID
  /// </summary>
  /// <param name="currSessionID"></param>
  /// <returns></returns>
  private bool HasCurrSessionIDInOnlineUserTb(string currSessionID)
  {
   if (currSessionID == null)
    return false;

   // 获取在线用户数据表
   DataTable onlineUserTb = this.m_onlineUserDS.Tables[TB_ONLINE_USER];

   // 统计用户昵称数量
   object count = onlineUserTb.Compute(
    String.Format("COUNT({0})", COL_CURR_SESSION_ID),
    String.Format("{0} = '{1}'", COL_CURR_SESSION_ID, currSessionID));

   return Convert.ToInt32(count) >= 1;
  }

  /// <summary>
  /// 判断在 SessionInfoTb 数据表中是否存在指定的用户昵称
  /// </summary>
  /// <param name="nickName">用户昵称</param>
  /// <returns></returns>
  private bool HasNickNameInSessionInfoTb(string nickName)
  {
   if (nickName == null)
    return false;

   // 获取 SessionInfo 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   // 统计用户昵称数量
   object count = sessionInfoTb.Compute(
    String.Format("COUNT({0})", COL_NICK_NAME),
    String.Format("{0} = '{1}'", COL_NICK_NAME, nickName));

   return Convert.ToInt32(count) >= 1;
  }

  /// <summary>
  /// 判断在 SessionInfoTb 数据表中是否存在指定的 SessionID
  /// </summary>
  /// <param name="sessionID">SessionID</param>
  /// <returns></returns>
  private bool HasSessionIDInSessionInfoTb(string sessionID)
  {
   if (sessionID == null)
    return false;

   // 获取 SessionInfo 数据表
   DataTable sessionInfoTb = this.m_onlineUserDS.Tables[TB_SESSION_INFO];

   // 统计用户昵称数量
   object count = sessionInfoTb.Compute(
    String.Format("COUNT({0})", COL_SESSION_ID),
    String.Format("{0} = '{1}'", COL_SESSION_ID, sessionID));

   return Convert.ToInt32(count) >= 1;
  }

  /// <summary>
  /// 获取在线用户信息
  /// </summary>
  /// <returns>在线用户集合</returns>
  public IList<OnlineUser> GetOnlineUsers()
  {
   IList<OnlineUser> onlineUsers = new List<OnlineUser>();

   // 获取 OnlineUserTb 数据表
   DataTable onlineUserTb = this.m_onlineUserDS.Tables[TB_ONLINE_USER];

   // 获取所有数据行,并排序
   DataRow[] rows = onlineUserTb.Select(null, String.Format("{0} DESC", COL_ACTIVE_TIME));

   foreach (DataRow r in rows)
   {
    OnlineUser onlineUser = PutObjectProperty(new OnlineUser(), r);
    onlineUsers.Add(onlineUser);
   }

   return onlineUsers;
  }

  /// <summary>
  /// 获取在线用户数量
  /// </summary>
  public int VisitorCount
  {
   get
   {
    return this.m_visitorCount;
   }
  }

  /// <summary>
  /// 获取超时时限
  /// </summary>
  private long TimeOutLimit
  {
   get
   {
    // 设置超时时间限制
    DateTime limit = DateTime.Now - TimeSpan.FromMinutes(this.TimeOutMinutes);

    return limit.Ticks;
   }
  }

  #region PutObjectProperty 设置对象实例
  /// <summary>
  /// 从数据源中读取数据,并设置到对象实例
  /// </summary>
  /// <param name="onlineUser">在线用户对象</param>
  /// <param name="row">数据源</param>
  /// <returns></returns>
  private OnlineUser PutObjectProperty(OnlineUser onlineUser, DataRow row)
  {
   if (onlineUser == null || row == null)
    return onlineUser;

   // 设置用户昵称
   onlineUser.NickName = row[COL_NICK_NAME] as string;
   // 最后活动时间
   onlineUser.ActiveTime = new DateTime((long)row[COL_ACTIVE_TIME]);
   // 最后活动页
   onlineUser.RequestURL = row[COL_REQUEST_URL] as string;

   if (onlineUser.NickName == "")
    onlineUser.NickName = null;

   return onlineUser;
  }
  #endregion

  #region OnlineUser 在线用户类
  /// <summary>
  /// OnlineUser 在线用户类
  /// </summary>
  public class OnlineUser
  {
   // 用户昵称
   private string m_nickName;
   // 最后活动时间
   private DateTime m_activeTime;
   // 最后请求地址
   private string m_requestURL;

   #region 类 OnlineUser 构造器
   /// <summary>
   /// 类 OnlineUser 默认构造器
   /// </summary>
   public OnlineUser()
   {
   }

   /// <summary>
   /// 类 OnlineUser 参数构造器
   /// </summary>
   /// <param name="nickName">用户昵称</param>
   public OnlineUser(string nickName)
   {
    this.NickName = nickName;
   }
   #endregion

   /// <summary>
   /// 设置或获取用户昵称
   /// </summary>
   public string NickName
   {
    set
    {
     this.m_nickName = value;
    }

    get
    {
     return this.m_nickName;
    }
   }

   /// <summary>
   /// 最后活动时间
   /// </summary>
   public DateTime ActiveTime
   {
    set
    {
     this.m_activeTime = value;
    }

    get
    {
     return this.m_activeTime;
    }
   }

   /// <summary>
   /// 最后请求地址
   /// </summary>
   public string RequestURL
   {
    set
    {
     this.m_requestURL = value;
    }

    get
    {
     return this.m_requestURL;
    }
   }
  }
  #endregion

  /// <summary>
  /// 工作模式
  /// </summary>
  public enum WorkMode
  {
   Asynchronous, MultiThreading
  };

  // 保存在线用户信息代理函数
  private delegate void MyDelegatePersist(string sessionID, OnlineUser onlineUser);
  // 删除在线用户信息代理函数
  private delegate void MyDelegateDelete(string sessionID);

  /// <summary>
  /// 保存工作线程
  /// </summary>
  private class PersistWorkThread
  {
   // SessionID
   private string m_sessionID = null;
   // 在线用户信息
   private OnlineUser m_onlineUser = null;

   #region 类 PersistWorkThread 构造器
   /// <summary>
   /// 类 PersistWorkThread 参数构造器
   /// </summary>
   /// <param name="sessionID">SessionID</param>
   /// <param name="onlineUser">在线用户信息</param>
   public PersistWorkThread(string sessionID, OnlineUser onlineUser)
   {
    // 设置 SessionID
    this.m_sessionID = sessionID;
    // 设置在线用户信息
    this.m_onlineUser = onlineUser;
   }
   #endregion

   /// <summary>
   /// 启动工作线程
   /// </summary>
   public void WorkStart()
   {
    OnlineUserService.Instance.Async_Persist(this.m_sessionID, this.m_onlineUser);
   }
  }
 }
}

原创粉丝点击