日期:2014-03-14  浏览次数:20372 次

最近所做的一个项目需要用到的在线用户列表,上网搜索了一下发现现有的解决方案对用户意外退出的处理均不是太理想。一般来说,用户离开系统的方式有三种:主动注销、会话超时、直接关闭浏览器,对于前两种,我们很容易便可将该用户从在线列表中清除,关键是第三种(很多用户都是直接关闭窗口的~~郁闷ing),程序无法捕获窗口关闭的精确时间,只能等到会话超时后在能将该用户清除出在线列表,假设我们设置会话超时时间为60分钟,而用户登陆系统随便浏览一个页面就以关闭浏览器的方式退出的话,我们要在将近1小时后才能从在线列表中将该用户清除出去(想象一下,系统显示n多人在线,可能除了你之外其他的n-1人都关机走人了,汗一个先```),而本文将尝试寻找一个解决方案把这种尴尬降至最低。
    我的大概思路是,给每在线用户增加一个RefreshTime属性,建立一个负责将当前用户的RefreshTime属性设置为当前时间的单独页面(Refresh.aspx),然后在系统的主要页面(也可以是所有页面)中通过xmlhttp不断地请求Refresh.aspx页面,一旦用户关闭了与本系统相关的所有窗口,即以直接关闭浏览器的方式退出系统,那么该用户的RefreshTime属性便不会自动更新了,我们再设置一个自动刷新的超时时间(这个要比会话超时短很多_refreshTimeout),当发现某用户超过_refreshTimeout的时间没有自动刷新,就能判定该用户已经以直接关闭浏览器的方式退出了。
    假设我们设置会话超时时间为60分钟,自动刷新超时时间为1分钟,在客户端通过xmlhttp每隔25秒(之所以不设1分钟,是防止网速慢的时候访问Refresh.aspx超时,个人感觉,不一定正确)访问一次Refresh.aspx页面,在用户登陆、用户注销、检测用户是否在线的时候都执行清理超时用户(包括会话超时和自动刷新超时)操作,这样一来,在线用户列表的统计误差就由60分钟降至1分钟了。

==========================================



具体实现如下:
1、 新建一个名为ActiveUser的类,存储单个活动用户数据。
 

/// <summary>
 /// 单个在线用户数据,无法继承此类。
 /// </summary> 
 public sealed class ActiveUser
 {  
  private readonly string _ticket;    //票据名称
  private readonly string _username;   //登陆用户名
  private readonly string _truename;   //登陆用户名
  private readonly string _roleid;    //角色
  private readonly DateTime _refreshtime;  //最新刷新时间
  private readonly DateTime _activetime;  //最新活动时间
  private readonly string _clientip;   //登陆IP
  
  public ActiveUser(string Ticket,string UserName,string TrueName,string RoleID,string ClientIP) {
   this._ticket=Ticket;
   this._username=UserName;
   this._truename=TrueName;
   this._roleid=RoleID;
   this._refreshtime=DateTime.Now;
   this._activetime=DateTime.Now;
   this._clientip=ClientIP;
  }

  public ActiveUser(string Ticket,string UserName,string TrueName,string RoleID,DateTime RefreshTime,DateTime ActiveTime,string ClientIP)  {
   this._ticket=Ticket;
   this._username=UserName;
   this._truename=TrueName;
   this._roleid=RoleID;
   this._refreshtime=RefreshTime;
   this._activetime=ActiveTime;
   this._clientip=ClientIP;
  }
  
  public string Ticket  { get{return _ticket;}  }
  public string UserName  { get{return _username;}  }
  public string TrueName  { get{return _truename;}  }
  public string RoleID  { get{return _roleid;}  }
  public DateTime RefreshTime { get{return _refreshtime;} }
  public DateTime ActiveTime { get{return _activetime;} }
  public string ClientIP  { get{return _clientip;}  }

 }


2、 新建一个名为PassPort的类,存储在线用户列表。

/// <summary>
 /// 单个在线用户数据,无法继承此类。
 /// </summary> 
 public sealed class ActiveUser
 {  
  private readonly string _ticket;    //票据名称
  private readonly string _username;   //登陆用户名
  private readonly string _truename;   //登陆用户名
  private readonly string _roleid;    //角色
  private readonly DateTime _refreshtime;  //最新刷新时间
  private readonly Date