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>
?/// 單個在線用戶數據,無法繼承此類。
?/// </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>
?/// 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的類,執行更新自動刷新時間操作。
?
/// <summary>
?/// 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腳本。。
/// <summary>
?/// 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
<appSettings>
?? <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中加入如下代碼:
string myTicket=(string)this.Page.Session["Ticket"];
?? 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事件加入如下代碼:
if(TextBox1.Text.Trim()=="")
?? {
??? //不能為空
??? 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中加入如下代碼:
OnlineUser.PassPort myPassPort= new OnlineUser.PassPort();
? myPassPort.Logout(this.Session["Ticket"].ToString());
?this.Session["Ticket"]="";
7、 添加一個名為Refresh.txt的文本文件,設置其內容為:
<%@ Register TagPrefix="cc2" Namespace="OnlineUser" Assembly="OnlineUser" %>
<%@ 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頁面
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三步正常,則大功告成,否則就再調試調試~~
==========================================================
轉載于:https://www.cnblogs.com/China-Dragon/archive/2009/12/04/1617273.html
總結
以上是生活随笔為你收集整理的ASP.NET在线用户列表精确版——解决用户意外退出在线列表无法及时更新问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LLBLGen 关于类型转换
- 下一篇: 在Netbeans下配置Tomcat m