A list of online users needed for a recent project. I searched the internet and found that the existing solution is not ideal for handling unexpected user exits. Generally, there are three ways for a user to leave the system: Active logout, Session Timeout, and direct browser close. For the first two ways, we can easily clear the user from the online list, the key is the third type (many users close the window directly ~~ The program cannot capture the exact time when the window is closed. It can only wait until the session times out before the user can be cleared from the online list. Suppose we set the Session Timeout time to 60 minutes, if a user logs on to the system to browse a page and closes the browser, it takes nearly an hour to clear the user from the online list (imagine, the system shows that N people are online, and n-1 people, except you, may have shut down and left. Khan is a pioneer '''), this article will try to find a solution to minimize this embarrassment. My general idea is to add a refreshtime attribute to each online user and set the refreshtime attribute of the current user to a separate page (refresh. aspx), and then continuously request refresh through XMLHTTP on the system's main page (or all pages. on the ASPX page, once the user closes all windows related to the system, that is, the user's refreshtime attribute is not automatically updated by directly disabling the browser, we will set another timeout value for automatic refresh (this is much shorter than the Session Timeout value _ refreshtimeout). When we find that a user does not refresh automatically after _ refreshtimeout, you can determine that the user has exited directly by closing the browser. Suppose we set the Session Timeout time to 60 minutes, and the automatic refresh timeout time to 1 minute. The client uses XMLHTTP to access refresh every 25 seconds (the reason is that 1 minute is not set to prevent access to refresh when the network speed is slow. aspx times out, personal feeling, not necessarily true) Access refresh once. on the ASPX page, when a user logs in, logs out, and checks whether the user is online, the user is cleared for timeout (including session timeout and automatic refresh timeout, the statistical error of the online user list is reduced from 60 minutes to 1 minute. ========================================================== = The specific implementation is as follows: 1. Create a new class named activeuser to store individual active user data.
- /// <Summary>
- /// The data of a single online user cannot be inherited.
- /// </Summary>
- Public sealed class activeuser
- {
- Private readonly string _ ticket; // ticket name
- Private readonly string _ username; // login Username
- Private readonly string _ truename; // login Username
- Private readonly string _ roleid; // role
- Private readonly datetime _ refreshtime; // The latest refresh time.
- Private readonly datetime _ activetime; // The latest activity time
- Private readonly string _ clientip; // logon 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
- }
Copy code
2. Create a new class named passport to store the online user list.
- /// <Summary>
- /// Passport storage online user list.
- /// </Summary>
- Public class passport
- {
- Private Static able _ activeusers;
- Private int _ activetimeout;
- Private int _ refreshtimeout;
- /// <Summary>
- /// Initialize the online user table.
- /// </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 (); // initialize the online user table
- // Activity timeout value initialization unit: minute
- Try {_ activetimeout = int. parse (configurationsettings. deleettimeout ["activetimeout"]);}
- Catch {_ activetimeout = 60 ;}
- // The time-out period for automatic refresh is measured in minutes.
- Try {_ refreshtimeout = int. parse (configurationsettings. receivettings ["refreshtimeout"]);}
- Catch {_ refreshtimeout = 1 ;}
- }
- // List of all users
- Public datatable activeusers
- {
- Get {return _ activeusers. Copy ();}
- }
- /// <Summary>
- /// New user login.
- /// </Summary>
- Public void login (activeuser user, bool singlelogin)
- {
- Deltimeout (); // clear the timeout user
- If (singlelogin ){
- // If a single user logs in, the user logged out is logged out.
- 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>
- /// The user logs out based on ticket or username.
- /// </Summary>
- Private void logout (string struserkey, bool byticket)
- {
- Deltimeout (); // clear the timeout user
- 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>
- /// The user logs out based on ticket.
- /// </Summary>
- /// <Param name = "strticket"> User Ticket to be logged out </param>
- Public void logout (string strticket ){
- This. logout (strticket, true );
- }
- /// <Summary>
- /// Clear the timeout user.
- /// </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>
- /// Update the user activity time.
- /// </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>
- /// Update the automatic refresh time of the system.
- /// </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>
- /// Obtain the active user by ticket.
- /// </Summary>
- Public activeuser singleuser_byticket (string strticket)
- {
- Return this. singleuser (strticket, true );
- }
- /// <Summary>
- /// Obtain the active user by username.
- /// </Summary>
- Public activeuser singleuser_byusername (string strusername)
- {
- Return this. singleuser (strusername, false );
- }
- /// <Summary>
- /// Determine whether the user is online by ticket.
- /// </Summary>
- Public bool isonline_byticket (string strticket)
- {
- Return (bool) (This. singleuser (strticket, true). Username! = "");
- }
- /// <Summary>
- /// Determine whether the user is online by username.
- /// </Summary>
- Public bool isonline_byusername (string strusername)
- {
- Return (bool) (This. singleuser (strusername, false). Username! = "");
- }
- }
Copy code
3. Create a new class inherited from placeholder named refresh and update the automatic refresh time.
- /// <Summary>
- /// Refresh performs the automatic update time operation.
- /// </Summary>
- Public class Refresh: placeholder
- {
- /// <Summary>
- /// Set the session name for storing ticket. The default value is 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 );
- }
- }
Copy code
4. Create a class inherited from placeholder named script to generate a JS script for executing XMLHTTP ..
- /// <Summary>
- /// Script: generate a JS script that executes XMLHTTP.
- /// </Summary>
- Public class Script: placeholder
- {
- /// <Summary>
- /// Set the interval for automatic JS refresh. The default value is 25 seconds.
- /// </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)
- {
- // Read the XMLHTTP access address from web. config
- String refreshurl = (string) configurationsettings. receivettings ["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 );
- }
- }
Copy code
Note that the above four classes belong to the same project named onlineuser. Their namespace is onlineuser. compile and generate a DLL. ========================================================== ======== Below I will briefly introduce the call method: 1. Create an Asp.net web application named onlineuserdemo. 2. Right-click the Toolbox tab of Vs, select [Add/Remove items], and navigate to onlineuser. DLL to add refresh and script to the toolbox. 3. Delete the automatically generated webform1.aspx and set web. config.
- <Deleetask>
- <Add key = "activetimeout" value = "30"/>
- <Add key = "refreshtimeout" value = "1"/>
- <Add key = "refreshurl" value = "Refresh. aspx"/>
- </Appsettings>
Copy code
4. Add an online account. aspx web form, add a script control to the form, a DataGrid Control (ID: datagrid1), and two hyperlink controls (respectively linked to login. aspx and logout. set the aspx and text attributes to "login" and "logout" respectively), adjust the positions of the four controls, go to codebehind, and add the following code to 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 {
- // If the current user cannot be found in the online user list, it is directed to the logout page.
- Response. Redirect ("logout. aspx ");
- }
- }
- Else {
- Response. Redirect ("login. aspx ");
- }
Copy code
5. Add a login name. aspx web form, add a label control (ID: label1) to the form, set the text attribute to "enter a user name", and then add a Textbox Control (ID: textbox1) and a button control (ID: button1), adjust their location, double-click the button1 control to go to codebehind, add the following code for the button1 click event:
- If (textbox1.text. Trim () = "")
- {
- // Cannot be blank
- String scriptstring = @ "<script language = JavaScript> ";
- Scriptstring + = @ "alert (" "enter a user name/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 ");
- }
Copy code
6. Add a logout name. aspx web form, add a hyperlink control to the form, pointing to login. set the aspx and text attributes to "re-login" and go to codebehind. Add the following code to page_load:
- Onlineuser. Passport mypassport = new onlineuser. Passport ();
- Mypassport. logout (this. session ["ticket"]. tostring ());
- This. session ["ticket"] = "";
Copy code
7. Upload a text file named refresh.txt and set its content:
- <% @ Register tagprefix = "CC2" namespace = "onlineuser" assembly = "onlineuser" %>
- <% @ Page %>
- <CC2: refresh id = "myrefresh" runat = "server"> </CC2: refresh>
- Rename refresh.txt to refresh. aspx
Copy code
8. compile and generate a project. ========================================================== ======= Perform the following functional tests: 1. Open your browser and enter http: // ip address of your machine/onlineuserdemo/login in the address bar. aspx 2. Enter a user name (Suppose test1) to log on and automatically go to online. on the ASPX page 3, find another machine in the same network segment (set your machine to a and this machine to B) and repeat the first step. 4. Enter a user name (for example, Test2) to log on and automatically go to online. ASPX page 51 aspx 5. Continuously refresh online on machine B. aspx. If we find that refreshtime of test1 is automatically updated every 25 seconds and activetime remains unchanged (at this time, do not refresh the page of machine A), the automatic refresh of machine A takes effect. 6. constantly refresh online on machine. aspx. If the refreshtime of Test2 is automatically updated every 25 seconds, but the activetime remains unchanged (at this time, the B server should not refresh the page), the automatic refresh of B server will take effect. 7. Directly shut down online on a machine (assuming. in the aspx browser window, refresh online on another machine (B. aspx. If Test2 is left after test1 is dropped in 1 minute, it indicates that the online user is successfully cleared through _ refreshtimeout. 8. If Steps 5, 6, and 7 are normal, you will be able to complete the debugging. Otherwise, you will debug the debugging again ~~