熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

ASP.NET優化在線用戶列表精確版

2022-06-13   來源: .NET編程 

  最近所做的一個項目需要用到的在線用戶列表上網搜索了一下發現現有的解決方案對用戶意外退出的處理均不是太理想一般來說用戶離開系統的方式有三種 主動注銷會話超時直接關閉浏覽器對於前兩種我們很容易便可將該用戶從在線列表中清除關鍵是第三種(很多用戶都是直接關閉窗口的~~郁悶 ing)程序無法捕獲窗口關閉的精確時間只能等到會話超時後在能將該用戶清除出在線列表假設我們設置會話超時時間為分鐘而用戶登陸系統隨便浏 覽一個頁面就以關閉浏覽器的方式退出的話我們要在將近小時後才能從在線列表中將該用戶清除出去(想象一下系統顯示n多人在線可能除了你之外其他的 n人都關機走人了汗一個先```)而本文將嘗試尋找一個解決方案把這種尴尬降至最低

    我的大概思路是給每在線用戶增加一個RefreshTime屬性建立一個負責將當前用戶的RefreshTime屬性設置為當前時間的單獨頁面 (Refreshaspx)然後在系統的主要頁面(也可以是所有頁面)中通過xmlx頁面一旦用戶關閉了 與本系統相關的所有窗口即以直接關閉浏覽器的方式退出系統那麼該用戶的RefreshTime屬性便不會自動更新了我們再設置一個自動刷新的超時時 間(這個要比會話超時短很多_refreshTimeout)當發現某用戶超過_refreshTimeout的時間沒有自動刷新就能判定該用戶已經 以直接關閉浏覽器的方式退出了

    假設我們設置會話超時時間為分鐘自動刷新超時時間為分鐘在客戶端通過xmlx超時個人感覺不一定正確)訪問一次Refreshaspx頁面在用戶登陸用戶注銷檢測用戶是否在線的時候都執行清理 超時用戶(包括會話超時和自動刷新超時)操作這樣一來在線用戶列表的統計誤差就由分鐘降至分鐘了

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

  具體實現如下

   新建一個名為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 Ticketstring UserNamestring TrueNamestring RoleIDstring ClientIP) {
   this_ticket=Ticket;
   this_username=UserName;
   this_truename=TrueName;
   this_roleid=RoleID;
   this_refreshtime=DateTimeNow;
   this_activetime=DateTimeNow;
   this_clientip=ClientIP;
}

  public ActiveUser(string Ticketstring UserNamestring TrueNamestring RoleIDDateTime RefreshTimeDateTime ActiveTimestring 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;} }

  }

   新建一個名為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;
    SystemType mystringtype;
    mystringtype = SystemTypeGetType(SystemString);
    SystemType mytimetype;
    mytimetype = SystemTypeGetType(SystemDateTime);
    myDataColumn = new DataColumn(Ticketmystringtype);
    _activeusersColumnsAdd(myDataColumn);
    myDataColumn = new DataColumn(UserNamemystringtype);
    _activeusersColumnsAdd(myDataColumn);
    myDataColumn = new DataColumn(TrueNamemystringtype);
    _activeusersColumnsAdd(myDataColumn);
    myDataColumn = new DataColumn(RoleIDmystringtype);
    _activeusersColumnsAdd(myDataColumn);   
    myDataColumn = new DataColumn(RefreshTimemytimetype);
    _activeusersColumnsAdd(myDataColumn);
    myDataColumn = new DataColumn(ActiveTimemytimetype);
    _activeusersColumnsAdd(myDataColumn);
    myDataColumn = new DataColumn(ClientIPmystringtype);
    _activeusersColumnsAdd(myDataColumn);  
   }
}

  public PassPort()
{
   userstableFormat(); //初始化在線用戶表


   //活動超時時間初始化 單位分鐘
   try { _activeTimeout=intParse(ConfigurationSettingsAppSettings[ActiveTimeout]); }
   catch{ _activeTimeout=; }  
   //自動刷新超時時間初始化 單位分鐘
   try { _refreshTimeout=intParse(ConfigurationSettingsAppSettings[RefreshTimeout]); }
   catch{ _refreshTimeout=; }  
}

  //全部用戶列表
public DataTable ActiveUsers
{
   get{return _activeusersCopy();}
}

  /// <summary>
/// 新用戶登陸
/// </summary>
public void Login(ActiveUser userbool SingleLogin)
{
   DelTimeOut(); //清除超時用戶
   if(SingleLogin){
    //若是單人登陸則注銷原來登陸的用戶
    thisLogout(userUserNamefalse);
   }
   DataRow myRow;
   try
   {
    myRow = _activeusersNewRow();   
    myRow[Ticket] = userTicketTrim();
    myRow[UserName] = userUserNameTrim();
    myRow[TrueName] = +userTrueNameTrim();
    myRow[RoleID] = +userRoleIDTrim();
    myRow[ActiveTime] = DateTimeNow;
    myRow[RefreshTime] = DateTimeNow;
    myRow[ClientIP] = userClientIPTrim();
    _activeusersRowsAdd(myRow);
   }
   catch(Exception e)
   {
    throw(new Exception(eMessage));
   }
   _activeusersAcceptChanges();
  
}

  /// <summary>
///用戶注銷根據Ticket或UserName
/// </summary>
private void Logout(string strUserKeybool byTicket)
{
   DelTimeOut(); //清除超時用戶
   strUserKey=strUserKeyTrim();
   string strExpr;  
   strExpr =byTicket ? Ticket= + strUserKey + : UserName= + strUserKey + ;
   DataRow[] curUser;
   curUser = _activeusersSelect(strExpr);
   if (curUserLength > )
   {
    for(int i = ; i < curUserLength; i ++)
    {
     curUser[i]Delete();
    }
   }
   _activeusersAcceptChanges();  
}

  /// <summary>
///用戶注銷根據Ticket
/// </summary>
/// <param name=strTicket>要注銷的用戶Ticket</param>
public void Logout(string strTicket){
   thisLogout(strTickettrue);
}

  /// <summary>
///清除超時用戶
/// </summary>
private bool DelTimeOut()
{  
   string strExpr;  
   strExpr = ActiveTime < + DateTimeNowAddMinutes( _activeTimeout) + or RefreshTime < +DateTimeNowAddMinutes( _refreshTimeout)+;  
   DataRow[] curUser;
   curUser = _activeusersSelect(strExpr);
   if (curUserLength > )
   {
    for(int i = ; i < curUserLength; i ++)
    {
     curUser[i]Delete();    
    }
   }
   _activeusersAcceptChanges();
   return true;
}

  /// <summary>
///更新用戶活動時間
/// </summary>
public void ActiveTime(string strTicket)
{
   DelTimeOut();
   string strExpr;
   strExpr = Ticket= + strTicket + ;
   DataRow[] curUser;
   curUser = _activeusersSelect(strExpr);
   if (curUserLength > )
   {
    for(int i = ; i < curUserLength; i ++)
    {
     curUser[i][ActiveTime]=DateTimeNow;
     curUser[i][RefreshTime]=DateTimeNow;
    }
   }
   _activeusersAcceptChanges();
}

  /// <summary>
///更新系統自動刷新時間
/// </summary>
public void RefreshTime(string strTicket)
{
   DelTimeOut();
   string strExpr;
   strExpr = Ticket= + strTicket + ;
   DataRow[] curUser;
   curUser = _activeusersSelect(strExpr);
   if (curUserLength > )
   {
    for(int i = ; i < curUserLength; i ++)
    {
     curUser[i][RefreshTime]=DateTimeNow;
    }
   }
   _activeusersAcceptChanges();
}

  private ActiveUser SingleUser(string strUserKeybool byTicket)
{
   strUserKey=strUserKeyTrim();
   string strExpr;
   ActiveUser myuser;
   strExpr =byTicket ? Ticket= + strUserKey + : UserName= + strUserKey + ;
   DataRow[] curUser;
   curUser = _activeusersSelect(strExpr);
   if (curUserLength > )
{
    string myTicket=(string)curUser[][Ticket];
    string myUser=(string)curUser[][UserName];
    string myName=(string)curUser[][TrueName];
    string myRoleID=(string)curUser[][RoleID];   
    DateTime myActiveTime=(DateTime)curUser[][ActiveTime];
    DateTime myRefreshtime=(DateTime)curUser[][RefreshTime];
    string myClientIP =(string)curUser[][ClientIP];
    myuser=new ActiveUser(myTicketmyUsermyNamemyRoleIDmyActiveTimemyRefreshtimemyClientIP);
   }
   else
   {
    myuser=new ActiveUser();   
   }
   return myuser;
}

  /// <summary>
///按Ticket獲取活動用戶
/// </summary>
public ActiveUser SingleUser_byTicket(string strTicket)
{
   return thisSingleUser(strTickettrue);
}

  /// <summary>
///按UserName獲取活動用戶
/// </summary>
public ActiveUser SingleUser_byUserName(string strUserName)
{
   return thisSingleUser(strUserNamefalse);
}

  /// <summary>
///按Ticket判斷用戶是否在線
/// </summary>
public bool IsOnline_byTicket(string strTicket)
{
   return (bool)(thisSingleUser(strTickettrue)UserName!=);
}

  /// <summary>
///按UserName判斷用戶是否在線
/// </summary>
public bool IsOnline_byUserName(string strUserName)
{
   return (bool)(thisSingleUser(strUserNamefalse)UserName!=);
}
}

   新建一個繼承自PlaceHolder名為Refresh的類執行更新自動刷新時間操作
/// <summary>
/// Refresh 執行更新自動刷新時間操作
/// </summary>
public class Refresh: PlaceHolder
{
/// <summary>
/// 設置存儲Ticket的Session名稱默認為Ticket
/// </summary>
public virtual string SessionName
{
   get{
    object obj = thisViewState[SessionName];
    if (obj != null){ return ((string) obj)Trim(); }
    return Ticket;
   }
   set{
    thisViewState[SessionName] = value;
   }
}

  protected override void Render(HtmlTextWriter writer)
{
   string myTicket=(string)thisPageSession[thisSessionName];
   if(myTicket!=null)
   {  
    PassPort myPass = new PassPort();
    myPassRefreshTime(myTicket);
    writerWrite(OK+DateTimeNowToString());
   }
   else{
    writerWrite(Sorry+DateTimeNowToString());
   }
   baseRender(writer);
}
}

   新建一個繼承自PlaceHolder名為Script的類生成執行xmlhttp的js腳本

  /// <summary>
/// Script 生成執行xmlhttp的js腳本
/// </summary>
public class Script: PlaceHolder
{
/// <summary>
/// 設置js自動刷新的間隔時間默認為
/// </summary>
public virtual int RefreshTime
{
   get
   {
    object obj = thisViewState[RefreshTime];
    if (obj != null){return intParse(((string) obj)Trim());}
    return ;
   }
   set
   {   
    thisViewState[RefreshTime] = value;
   }
}

  protected override void Render(HtmlTextWriter writer)
{
   //從nfig中讀取xmlhttp的訪問地址
   string refreshUrl=(string)ConfigurationSettingsAppSettings[refreshUrl];
   string scriptString = @ <script language=JavaScript>+writerNewLine;
   scriptString += @ windowattachEvent(onload +thisClientID+@_postRefresh);+writerNewLine;
   scriptString += @ var +thisClientID+@_xmlhttp=null;+writerNewLine;
   scriptString += @ function +thisClientID+@_postRefresh(){+writerNewLine;
   scriptString += @   var +thisClientID+@_xmlhttp = new ActiveXObject(MsxmlXMLHTTP);+writerNewLine;
   scriptString += @   +thisClientID+@_xmlhttpOpen(POST +refreshUrl+@ false);+writerNewLine;
   scriptString += @   +thisClientID+@_xmlhttpSend();+writerNewLine;
   scriptString += @   var refreshStr= +thisClientID+@_xmlhttpresponseText;+writerNewLine;
   
   scriptString += @   try {+writerNewLine;
   scriptString += @    var refreshStr=refreshStr;+writerNewLine;
   //scriptString += @    alert(refreshStr);+writerNewLine;
   scriptString += @   }+writerNewLine;
   scriptString += @   catch(e) {}+writerNewLine;
   scriptString += @   setTimeout(+thisClientID+@_postRefresh()+thisRefreshTimeToString()+@);+writerNewLine;
   scriptString += @ }+writerNewLine;
   scriptString += @<;
   scriptString += @/;
   scriptString += @script>+writerNewLine;

  writerWrite(writerNewLine);
   writerWrite(scriptString);
   writerWrite(writerNewLine);
   baseRender(writer);
}
}

  注意以上四個類同屬於一個名為OnlineUser的工程他們的命名空間為OnlineUser編譯生成一個dll

  下面我簡單介紹一下調用方法

   新建一個名為OnlineUserDemo的 web應用程序
在vs的工具箱選項卡上右擊選擇[添加/移除項]浏覽定位到OnlineUserdll確定即可把Refresh 和Script添加到工具箱
把自動生成的WebFormaspx刪除並設置nfig
<appSettings>
   <add key=ActiveTimeout value= />
   <add key=RefreshTimeout value= />
   <add key=refreshUrl value=refreshaspx />
</appSettings>
添加一個名為Onlineaspx的web窗體給該窗體添加一個Script控件一個DataGrid控件(id為DataGrid兩個 HyperLink控件(分別鏈接到loginaspx和logoutaspxtext屬性分別設置為登陸注銷調整好四個控件的位 置轉到codebehind在Page_Load中加入如下代碼
string myTicket=(string)thisPageSession[Ticket];
   if(myTicket!=null)
   {
    OnlineUserPassPort myPassPort= new OnlineUserPassPort();
    if(myPassPortIsOnline_byTicket(thisSession[Ticket]ToString()))
    {
     myPassPortActiveTime(thisSession[Ticket]ToString());
     DataGridDataSource=myPassPortActiveUsers;
     DataGridDataBind();
    }
    else{
     //若在線用戶列表中找不到當前用戶則定向到注銷頁面
     ResponseRedirect(Logoutaspx);
    }
   }
   else{
    ResponseRedirect(Loginaspx);
   }
添加一個名為loginaspx的web窗體給該窗體添加一個label控件(id為Label設置text屬性為輸入一個用戶名再添加 一個textbox控件(id為TextBox)和一個button控件(id為Button調整好他們的位置雙擊Button控件轉到 codebehind為Button的Click事件加入如下代碼
if(TextBoxTextTrim()==)
   {
    //不能為空
    String scriptString = @<script language=JavaScript>;
    scriptString += @alert(輸入一個用戶名\n);;
    scriptString += @historygo();;
    scriptString += @<;
    scriptString += @/;
    scriptString += @script>;
    if(!thisPageIsStartupScriptRegistered(Startup))
     thisPageRegisterStartupScript(Startup scriptString);
   }
   else{
    OnlineUserPassPort myPassPort= new OnlineUserPassPort();
    string myTicket=DateTimeNowToString(yyyyMMddHHmmss);
    string myUser=TextBoxTextTrim();
    string myClintIP=thisRequestUserHostAddress;
    thisSession[Ticket]=myTicket;
    OnlineUserActiveUser myActiveUser=new OnlineUserActiveUser(myTicketmyUsermyUsertestmyClintIP);
    myPassPortLogin(myActiveUsertrue);
    ResponseRedirect(Onlineaspx);
   }
添加一個名為logoutaspx的web窗體給該窗體添加一個HyperLink控件指向loginaspxtext屬性設置為重登陸轉到codebehind在Page_Load中加入如下代碼
OnlineUserPassPort myPassPort= new OnlineUserPassPort();
myPassPortLogout(thisSession[Ticket]ToString());
thisSession[Ticket]=;

   添加一個名為Refreshtxt的文本文件設置其內容為
<%@ Register TagPrefix=cc Namespace=OnlineUser Assembly=OnlineUser %>
<%@ Page %>
<cc:Refresh id=myRefresh runat=server></cc:Refresh>
把Refreshtxt改名為Refreshaspx

   編譯生成工程

  ===============================================
下面進行功能測試

   打開浏覽器在地址欄輸入
x
輸入一個用戶名(假設是test)登陸自動轉到onlineaspx頁面
找同網段的另外一台機器(設你的機器為a這台機器為b)重復執行第一步


輸入一個用戶名(假設是test)登陸自動轉到onlineaspx頁面
在b機器不斷刷新onlineaspx若發現test用戶RefreshTime每過秒自動更新一次而ActiveTime不變(這個時候a機器不要刷新頁面啊)則證明a機器的自動刷新生效
在a機器不斷刷新onlineaspx若發現test用戶RefreshTime每過秒自動更新一次而ActiveTime不變(這個時候b機器不要刷新頁面啊)則證明b機器的自動刷新生效
直接關閉一台機器(假設是a)上的onlineaspx浏覽窗口在另一台機器(就是b啦)上刷新onlineaspx若發現分鐘後test掉線在線用戶只剩下test證明通過_refreshTimeout清除在線用戶成功
三步正常則大功告成否則就再調試調試~~


From:http://tw.wingwit.com/Article/program/net/201311/13149.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.