[转]ASP.NET在线用户列表精确版—解决用户意外退出在线列表无法及时更新问
ASP.NET在线用户列表精确版—解决用户意外退出在线列表无法及时更新问题
我的大概思路是,给每在线用户增加一个RefreshTime属性,建立一个负责将当前用户的RefreshTime属性设置为当前时间的单独页面(Refresh.aspx),然后在系统的主要页面(也可以是所有页面)中通过xmlhttp不断地请求Refresh.aspx页面,一旦用户关闭了与本系统相关的所有窗口,即以直接关闭浏览器的方式退出系统,那么该用户的RefreshTime属性便不会自动更新了,我们再设置一个自动刷新的超时时间(这个要比会话超时短很多_refreshTimeout),当发现某用户超过_refreshTimeout的时间没有自动刷新,就能判定该用户已经以直接关闭浏览器的方式退出了。
假设我们设置会话超时时间为60分钟,自动刷新超时时间为1分钟,在客户端通过xmlhttp每隔25秒(之所以不设1分钟,是防止网速慢的时候访问Refresh.aspx超时,个人感觉,不一定正确)访问一次Refresh.aspx页面,在用户登陆、用户注销、检测用户是否在线的时候都执行清理超时用户(包括会话超时和自动刷新超时)操作,这样一来,在线用户列表的统计误差就由60分钟降至1分钟了。
==========================================
具体实现如下:
1、 新建一个名为ActiveUser的类,存储单个活动用户数据。
/// 单个在线用户数据,无法继承此类。
/// </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;} }//51aspx.com
}
2、 新建一个名为PassPort的类,存储在线用户列表。
/// PassPort 存储在线用户列表。
/// </summary>
public class PassPort
{
private static DataTable _activeusers;
private int _activeTimeout;
private int _refreshTimeout;
/**//// <summary>
/// 初始化在线用户表。
/// </summary>
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();}
}
/**//// <summary>
/// 新用户登陆。
/// </summary>
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();
}
/**//// <summary>
///用户注销,根据Ticket或UserName。
/// </summary>
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();
}
/**//// <summary>
///用户注销,根据Ticket。
/// </summary>
/// <param name="strTicket">要注销的用户Ticket</param>
public void Logout(string strTicket){
this.Logout(strTicket,true);
}
/**//// <summary>
///清除超时用户。
/// </summary>
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;
}
/**//// <summary>
///更新用户活动时间。
/// </summary>
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();
}
/**//// <summary>
///更新系统自动刷新时间。
/// </summary>
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;
}
/**//// <summary>
///按Ticket获取活动用户。
/// </summary>
public ActiveUser SingleUser_byTicket(string strTicket)
{
return this.SingleUser(strTicket,true);
}
/**//// <summary>
///按UserName获取活动用户。
/// </summary>
public ActiveUser SingleUser_byUserName(string strUserName)
{
return this.SingleUser(strUserName,false);
}
/**//// <summary>
///按Ticket判断用户是否在线。
/// </summary>
public bool IsOnline_byTicket(string strTicket)
{
return (bool)(this.SingleUser(strTicket,true).UserName!="");
}
/**//// <summary>
///按UserName判断用户是否在线。
/// </summary>
public bool IsOnline_byUserName(string strUserName)
{
return (bool)(this.SingleUser(strUserName,false).UserName!="");
}
}
3、 新建一个继承自PlaceHolder名为Refresh的类,执行更新自动刷新时间操作。
/// Refresh 执行更新自动刷新时间操作。
/// </summary>
public class Refresh: PlaceHolder
{
/**//// <summary>
/// 设置存储Ticket的Session名称,默认为Ticket。
/// </summary>
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脚本。
/// </summary>
public class Script: PlaceHolder
{
/**//// <summary>
/// 设置js自动刷新的间隔时间,默认为25秒。
/// </summary>
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 = @" <script language=""JavaScript"">"+writer.NewLine;
scriptString += @" window.attachEvent(""onload"", "+this.ClientID+@"_postRefresh);"+writer.NewLine;
scriptString += @" var "+this.ClientID+@"_xmlhttp=null;"+writer.NewLine;
scriptString += @" function "+this.ClientID+@"_postRefresh(){"+writer.NewLine;
scriptString += @" var "+this.ClientID+@"_xmlhttp = new ActiveXObject(""Msxml2.XMLHTTP"");"+writer.NewLine;
scriptString += @" "+this.ClientID+@"_xmlhttp.Open(""POST"", """+refreshUrl+@""", false);"+writer.NewLine;
scriptString += @" "+this.ClientID+@"_xmlhttp.Send();"+writer.NewLine;
scriptString += @" var refreshStr= "+this.ClientID+@"_xmlhttp.responseText;"+writer.NewLine;
scriptString += @" try {"+writer.NewLine;
scriptString += @" var refreshStr2=refreshStr;"+writer.NewLine;
//scriptString += @" alert(refreshStr2);"+writer.NewLine;
scriptString += @" }"+writer.NewLine;
scriptString += @" catch(e) {}"+writer.NewLine;
scriptString += @" setTimeout("""+this.ClientID+@"_postRefresh()"","+this.RefreshTime.ToString()+@"000);"+writer.NewLine;
scriptString += @" }"+writer.NewLine;
scriptString += @"<";
scriptString += @"/";
scriptString += @"script>"+writer.NewLine;
writer.Write(writer.NewLine);
writer.Write(scriptString);
writer.Write(writer.NewLine);
base.Render(writer);
}
}
注意以上四个类同属于一个名为OnlineUser的工程,他们的命名空间为OnlineUser,编译生成一个dll。
===============================================
下面我简单介绍一下调用方法:
1、 新建一个名为OnlineUserDemo的asp.net web应用程序
2、 在vs的工具箱选项卡上右击,选择[添加/移除项],浏览定位到OnlineUser.dll,确定即可把Refresh 和Script添加到工具箱。
3、 把自动生成的WebForm1.aspx删除,并设置web.config
<add key="ActiveTimeout" value="30" />
<add key="RefreshTimeout" value="1" />
<add key="refreshUrl" value="refresh.aspx" />
</appSettings>
4、 添加一个名为Online.aspx的web窗体,给该窗体添加一个Script控件,一个DataGrid控件(id为DataGrid1),两个HyperLink控件(分别链接到login.aspx和logout.aspx,text属性分别设置为“登陆”和“注销”),调整好四个控件的位置,转到codebehind,在Page_Load中加入如下代码:
if(myTicket!=null)
{
OnlineUser.PassPort myPassPort= new OnlineUser.PassPort();
if(myPassPort.IsOnline_byTicket(this.Session["Ticket"].ToString()))
{
myPassPort.ActiveTime(this.Session["Ticket"].ToString());
DataGrid1.DataSource=myPassPort.ActiveUsers;
DataGrid1.DataBind();
}
else{
//若在线用户列表中找不到当前用户,则定向到注销页面
Response.Redirect("Logout.aspx");
}
}
else{
Response.Redirect("Login.aspx");
}
5、 添加一个名为login.aspx的web窗体,给该窗体添加一个label控件(id为Label1),设置text属性为“输入一个用户名”,再添加一个textbox控件(id为TextBox1)和一个button控件(id为Button1),调整好他们的位置,双击Button1控件转到codebehind,为Button1的Click事件加入如下代码:
{
//不能为空
String scriptString = @"<script language=JavaScript>";
scriptString += @"alert(""输入一个用户名\n"");";
scriptString += @"history.go(-1);";
scriptString += @"<";
scriptString += @"/";
scriptString += @"script>";
if(!this.Page.IsStartupScriptRegistered("Startup"))
this.Page.RegisterStartupScript("Startup", scriptString);
}
else{
OnlineUser.PassPort myPassPort= new OnlineUser.PassPort();
string myTicket=DateTime.Now.ToString("yyyyMMddHHmmss");
string myUser=TextBox1.Text.Trim();
string myClintIP=this.Request.UserHostAddress;
this.Session["Ticket"]=myTicket;
OnlineUser.ActiveUser myActiveUser=new OnlineUser.ActiveUser(myTicket,myUser,myUser,"test",myClintIP);
myPassPort.Login(myActiveUser,true);
Response.Redirect("Online.aspx");
}
6、 添加一个名为logout.aspx的web窗体,给该窗体添加一个HyperLink控件,指向login.aspx,text属性设置为“重登陆”转到codebehind,在Page_Load中加入如下代码:
myPassPort.Logout(this.Session["Ticket"].ToString());
this.Session["Ticket"]="";
7、 添加一个名为Refresh.txt的文本文件,设置其内容为:
<%@ Page %>
<cc2:Refresh id="myRefresh" runat="server"></cc2:Refresh>
把Refresh.txt改名为Refresh.aspx
8、 编译生成工程。
===============================================
下面进行功能测试:
1、 打开浏览器,在地址栏输入
http://你机器的IP地址/onlineuserdemo/Login.aspx
2、 输入一个用户名(假设是test1)登陆,自动转到online.aspx页面
3、 找同网段的另外一台机器(设你的机器为a,这台机器为b),重复执行第一步。
4、 输入一个用户名(假设是test2)登陆,自动转到online.aspx页面51aspx
5、 在b机器不断刷新online.aspx,若发现test1用户RefreshTime每过25秒自动更新一次而ActiveTime不变(这个时候a机器不要刷新页面啊),则证明a机器的自动刷新生效。
6、 在a机器不断刷新online.aspx,若发现test2用户RefreshTime每过25秒自动更新一次而ActiveTime不变(这个时候b机器不要刷新页面啊),则证明b机器的自动刷新生效。
7、 直接关闭一台机器(假设是a)上的online.aspx浏览窗口,在另一台机器(就是b啦)上刷新online.aspx,若发现1分钟后test1掉线在线用户只剩下test2,证明通过_refreshTimeout清除在线用户成功。
8、 若5、6、7三步正常,则大功告成,否则就再调试调试~~
[转]ASP.NET在线用户列表精确版—解决用户意外退出在线列表无法及时更新问相关推荐
- ASP.NET在线用户列表精确版——解决用户意外退出在线列表无法及时更新问题
最近所做的一个项目需要用到的在线用户列表,上网搜索了一下发现现有的解决方案对用户意外退出的处理均不是太理想.一般来说,用户离开系统的方式有三种:主动注销.会话超时.直接关闭浏览器,对于前两种,我们很容 ...
- 解决用户意外退出在线列表无法及时更新问题2(转载)
1 一般来说,用户离开系统的方式有三种:主动注销.会话超时.直接关闭浏览器,对于前两种,我们很容易便可将该用户从在线列表中清除,关键是第三种(很多用户都是直接关闭窗口的~~郁闷ing),程序无法捕获窗 ...
- uniapp 小程序video列表页 解决 ios 详情页返回列表页,列表页重载问题。
安卓手机不用考虑,但是在ios系统,会出现详情返回列表,列表页会重载0 首先官方推荐video 列表不超过3个video组件,再次我们用image代替video,点击是在切换出video插件. ` / ...
- asp.net登陆数据库的错误解决
asp.net登陆数据库的错误解决 用户 'abc\ASPNET' 登录失败. > 说明: 执行当前 Web 请求期间,出现未处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的 ...
- 今日头条首次改进DQN网络,解决推荐中的在线广告投放问题
(图片付费下载自视觉中国) 作者 | 深度传送门 来源 | 深度传送门(ID:gh_5faae7b50fc5) [导读]本文主要介绍今日头条推出的强化学习应用在推荐的最新论文[1],首次改进DQN网络 ...
- ASP.NET常见错误,原因及解决方法(2003版)_不断更新.....
[标题] ASP.NET常见错误,原因及解决方法[错误提示] 异常详细信息: System.Net.WebException: 请求因 HTTP 状态 401 失败:Un ...
- cookie 在线人数列表_前端学习随笔2 在线简历
一 心得体会 今天的任务是利用HTML制作一份在线简历,先不考虑样式,只从结构和语义化方面着手.对于已经有了前端b编程经验的我来说挺简单的,但是我还是当作我是一个刚入门的菜鸟,在学习过程中发现其实还 ...
- [Asp.net]常见word,excel,ppt,pdf在线预览方案,有图有真相,总有一款适合你!...
[Asp.net]常见word,excel,ppt,pdf在线预览方案,有图有真相,总有一款适合你! 引言 之前项目需要,查找了office文档在线预览的解决方案,顺便记录一下,方便以后查询. 方案一 ...
- mysql基础+用户管理+完增日志备份+innobackupex在线全增备份
官网:http://dev.mysql.com/downloads/mysql 配置mysql数据库: 下载源码包解压:切换到目录下 yum -y install mysql-community-*. ...
- Win7 IIS7.5运行ASP时出现500错误的解决办法
http 500内部服务器错误说明IIS服务器无法解析ASP代码,下面为大家介绍下Win7 IIS7.5运行ASP时出现500错误的解决办法 http 500内部服务器错误说明IIS服务器无法解析AS ...
最新文章
- AI:2020年6月21日北京智源大会演讲分享之15:15-15:40黄萱菁教授《自然语言处理中的表示学习》
- 超适合新手的基础Linux命令
- Atitit。DD dragdrop拖拽功能c#.net java swing的对比与实现总结
- Codeforces Round #548 (Div. 2) A. Even Substrings
- linux网卡固件名,修改CentOS7网卡名称为传统名称eth0格式
- linux 多个cpu使用率,统计多台linux的CPU使用率
- 服务器安装系统教程进光盘界面,iso光盘系统怎么安装系统教程
- 记录几种常用编码方式:BASE64、MD5
- centos7 配置anaconda及anaconda常用命令
- 这个爬虫是你五一假期所需要的!
- OpenGL超级宝典 绘制第一个三角形
- 广州计算机公办学校有哪些,广州各区小学对口中学列表,小学对口哪些初中?这里有名单大全...
- 华为研发工程师笔试编程题
- 华中科技大学计算机学院任思浩,华中科技大学2018年本科特优生名单
- python爬虫爬网页源码保存到本地_python爬虫网页图片并保存到本地
- vue 中 自定义按钮实现video暂停和播放
- 量化股票交易接口如何一键执行委托下单?
- Stacked Hourglass Networks简析
- 蚂蚁金服张洁:基于深度学习的支付宝人脸识别技术解秘
- 96Boards MIPI CSI Camera Mezzanine V2.1