ASP.NET在线用户列表精确版——解决用户意外退出在线列表无法及时更新问题

3 篇文章 0 订阅
【原创作者】:丛兴滋(cncxz)[E-mail:cncxz@126.com] 【关 键 词】:xmlhttp ASP.NET在线用户列表 关闭浏览器 精确统计 单人登陆 最近所做的一个项目需要用到的在线用户列表,上网搜索了一下发现现有的解决方案对用户意外退出的处理均不是太理想。一般来说,用户离开系统的方式有三种:主动注销、会话超时、直接关闭浏览器,对于前两种,我们很容易便可将该用户从在线列表中清除,关键是第三种(很多用户都是直接关闭窗口的~~郁闷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的类,存储单个活动用户数据。 /// /// 单个在线用户数据,无法继承此类。 /// 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的类,存储在线用户列表。 /// /// PassPort 存储在线用户列表。 /// public class PassPort { private static DataTable _activeusers; private int _activeTimeout; private int _refreshTimeout; /// /// 初始化在线用户表。 /// private void userstableFormat() { if(_activeusers==null) { _activeusers = new DataTable("ActiveUsers"); DataColumn myDataColumn; System.Type mystringtype; mystringtype = System.Type.GetType("System.String"); System.Type mytimetype; mytimetype = System.Type.GetType("System.DateTime"); myDataColumn = new DataColumn("Ticket",mystringtype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("UserName",mystringtype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("TrueName",mystringtype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("RoleID",mystringtype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("RefreshTime",mytimetype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("ActiveTime",mytimetype); _activeusers.Columns.Add(myDataColumn); myDataColumn = new DataColumn("ClientIP",mystringtype); _activeusers.Columns.Add(myDataColumn); } } public PassPort() { userstableFormat(); //初始化在线用户表 //活动超时时间初始化 单位:分钟 try { _activeTimeout=int.Parse(ConfigurationSettings.AppSettings["ActiveTimeout"]); } catch{ _activeTimeout=60; } //自动刷新超时时间初始化 单位:分钟 try { _refreshTimeout=int.Parse(ConfigurationSettings.AppSettings["RefreshTimeout"]); } catch{ _refreshTimeout=1; } } //全部用户列表 public DataTable ActiveUsers { get{return _activeusers.Copy();} } /// /// 新用户登陆。 /// public void Login(ActiveUser user,bool SingleLogin) { DelTimeOut(); //清除超时用户 if(SingleLogin){ //若是单人登陆则注销原来登陆的用户 this.Logout(user.UserName,false); } DataRow myRow; try { myRow = _activeusers.NewRow(); myRow["Ticket"] = user.Ticket.Trim(); myRow["UserName"] = user.UserName.Trim(); myRow["TrueName"] = ""+user.TrueName.Trim(); myRow["RoleID"] = ""+user.RoleID.Trim(); myRow["ActiveTime"] = DateTime.Now; myRow["RefreshTime"] = DateTime.Now; myRow["ClientIP"] = user.ClientIP.Trim(); _activeusers.Rows.Add(myRow); } catch(Exception e) { throw(new Exception(e.Message)); } _activeusers.AcceptChanges(); } /// ///用户注销,根据Ticket或UserName。 /// private void Logout(string strUserKey,bool byTicket) { DelTimeOut(); //清除超时用户 strUserKey=strUserKey.Trim(); string strExpr; strExpr =byTicket ? "Ticket='" + strUserKey +"'" : "UserName='" + strUserKey + "'"; DataRow[] curUser; curUser = _activeusers.Select(strExpr); if (curUser.Length >0 ) { for(int i = 0; i < curUser.Length; i ++) { curUser[i].Delete(); } } _activeusers.AcceptChanges(); } /// ///用户注销,根据Ticket。 /// /// 要注销的用户Ticket public void Logout(string strTicket){ this.Logout(strTicket,true); } /// ///清除超时用户。 /// private bool DelTimeOut() { string strExpr; strExpr = "ActiveTime < '" + DateTime.Now.AddMinutes( 0 - _activeTimeout) + "'or RefreshTime < '"+DateTime.Now.AddMinutes( 0 - _refreshTimeout)+"'"; DataRow[] curUser; curUser = _activeusers.Select(strExpr); if (curUser.Length >0 ) { for(int i = 0; i < curUser.Length; i ++) { curUser[i].Delete(); } } _activeusers.AcceptChanges(); return true; } /// ///更新用户活动时间。 /// public void ActiveTime(string strTicket) { DelTimeOut(); string strExpr; strExpr = "Ticket='" + strTicket + "'"; DataRow[] curUser; curUser = _activeusers.Select(strExpr); if (curUser.Length >0 ) { for(int i = 0; i < curUser.Length; i ++) { curUser[i]["ActiveTime"]=DateTime.Now; curUser[i]["RefreshTime"]=DateTime.Now; } } _activeusers.AcceptChanges(); } /// ///更新系统自动刷新时间。 /// public void RefreshTime(string strTicket) { DelTimeOut(); string strExpr; strExpr = "Ticket='" + strTicket + "'"; DataRow[] curUser; curUser = _activeusers.Select(strExpr); if (curUser.Length >0 ) { for(int i = 0; i < curUser.Length; i ++) { curUser[i]["RefreshTime"]=DateTime.Now; } } _activeusers.AcceptChanges(); } private ActiveUser SingleUser(string strUserKey,bool byTicket) { strUserKey=strUserKey.Trim(); string strExpr; ActiveUser myuser; strExpr =byTicket ? "Ticket='" + strUserKey +"'" : "UserName='" + strUserKey + "'"; DataRow[] curUser; curUser = _activeusers.Select(strExpr); if (curUser.Length >0 ) { string myTicket=(string)curUser[0]["Ticket"]; string myUser=(string)curUser[0]["UserName"]; string myName=(string)curUser[0]["TrueName"]; string myRoleID=(string)curUser[0]["RoleID"]; DateTime myActiveTime=(DateTime)curUser[0]["ActiveTime"]; DateTime myRefreshtime=(DateTime)curUser[0]["RefreshTime"]; string myClientIP =(string)curUser[0]["ClientIP"]; myuser=new ActiveUser(myTicket,myUser,myName,myRoleID,myActiveTime,myRefreshtime,myClientIP); } else { myuser=new ActiveUser("","","","",""); } return myuser; } /// ///按Ticket获取活动用户。 /// public ActiveUser SingleUser_byTicket(string strTicket) { return this.SingleUser(strTicket,true); } /// ///按UserName获取活动用户。 /// public ActiveUser SingleUser_byUserName(string strUserName) { return this.SingleUser(strUserName,false); } /// ///按Ticket判断用户是否在线。 /// public bool IsOnline_byTicket(string strTicket) { return (bool)(this.SingleUser(strTicket,true).UserName!=""); } /// ///按UserName判断用户是否在线。 /// public bool IsOnline_byUserName(string strUserName) { return (bool)(this.SingleUser(strUserName,false).UserName!=""); } } 3、 新建一个继承自PlaceHolder名为Refresh的类,执行更新自动刷新时间操作。 /// /// Refresh 执行更新自动刷新时间操作。 /// public class Refresh: PlaceHolder { /// /// 设置存储Ticket的Session名称,默认为Ticket。 /// public virtual string SessionName { get{ object obj1 = this.ViewState["SessionName"]; if (obj1 != null){ return ((string) obj1).Trim(); } return "Ticket"; } set{ this.ViewState["SessionName"] = value; } } protected override void Render(HtmlTextWriter writer) { string myTicket=(string)this.Page.Session[this.SessionName]; if(myTicket!=null) { PassPort myPass = new PassPort(); myPass.RefreshTime(myTicket); writer.Write("OK:"+DateTime.Now.ToString()); } else{ writer.Write("Sorry:"+DateTime.Now.ToString()); } base.Render(writer); } } 4、 新建一个继承自PlaceHolder名为Script的类,生成执行xmlhttp的js脚本。。 /// /// Script 生成执行xmlhttp的js脚本。 /// public class Script: PlaceHolder { /// /// 设置js自动刷新的间隔时间,默认为25秒。 /// public virtual int RefreshTime { get { object obj1 = this.ViewState["RefreshTime"]; if (obj1 != null){return int.Parse(((string) obj1).Trim());} return 25; } set { this.ViewState["RefreshTime"] = value; } } protected override void Render(HtmlTextWriter writer) { //从web.config中读取xmlhttp的访问地址 string refreshUrl=(string)ConfigurationSettings.AppSettings["refreshUrl"]; string scriptString = @"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值