Discuz!NT 在线用户功能简介
??? 聲明:本文內容純屬個人觀點,官方保留最終解釋
???? 在上文(Discuz!NT URL地址重寫) 中, 聊到了“在線用戶”功能,因為當時介紹的重點不是“在線”
那一塊,所以沒做深入介紹。這就為今天這篇文章埋下了“伏筆”。因為在線這個功能太重要了,大家不妨
用VS打開我們產品的最新源碼,然后搜索一下“OnlineUsers.”這個內容就會看到它在產品中被使用的“頻
率”。
?
???? 好了,言歸正傳,下面就開始接著上一篇文章中所說的“OnlineUsers.ResetOnlineList();” 方法介紹
一下用戶在線功能。
???? 首先請大家打開Discuz.Forum這個項目,從中找到"OnlineUsers.cs"這個文件,打開它找到
"ResetOnlineList()"這個函數,它的代碼如下:
???
?1??/**////?<summary>?2??///?復位在線表,?如果系統未重啟,?僅是應用程序重新啟動,?則不會重新創建
?3??///?</summary>
?4??///?<returns></returns>
?5??public?static?int?ResetOnlineList()
?6??{
?7???try
?8???{
?9????//?取得在線表最后一條記錄的tickcount字段?(因為本功能不要求特別精確)
10??????????????????//int?tickcount?=?DatabaseProvider.GetInstance().GetLastTickCount();
11????//?如果距離現在系統運行時間小于10分鐘
12????if?(System.Environment.TickCount?<?600000)
13????{
14?????return?InitOnlineList();
15????}
16????return?-1;
17???}
18???catch
19???{
20????try
21????{
22?????return?InitOnlineList();
23????}
24????catch
25????{
26?????return?-1;
27????}
28???}
29
30??}
31
32
????? 這個函數本身就是在系統啟動之后的10分鐘內運行InitOnlineList函數,而為什么是10分鐘(
不是別的時間段呢?),主要是因為這個值是個“估計值”,因為在線功能是系統的核心功能之一,
換句話說,系統在啟動10分鐘內,只要是用戶在前臺進行操作,絕對會用到這個核心功能,而這個
功能本身是依賴于數據庫中的“dnt_onlines"表的(如果大家想了解這個表的結構,可以下載我們
產品的“數據字典”,里面有這方面內容的介紹)。所以創建(復位)和初始化這個數據表的責任就
交給了ResetOnlineList()這個函數(因為它是在HttpModule.cs中被綁定的,可以看作是系統運行
的起點,詳情見上文)。
???? 好了,即然清楚了這個函數的作用,不妨再了解一下代碼中InitOnlineList()方法,它的作用
就是運行下面這個SQL語句:
?
?1//該函數位于Discuz.Data.SqlServer項目中的UserManage.cs文件中?2?public?int?CreateOnlineTable()?
?3????????{
?4????????????try
?5????????????{
?6????????????????StringBuilder?sb?=?new?StringBuilder();
?7????????????????sb.Append("IF?EXISTS?(SELECT?*?FROM?SYSOBJECTS?WHERE?id?=?object_id(N'[dnt_online]')?AND?OBJECTPROPERTY(id,?N'IsUserTable')?=?1)?DROP?TABLE?[dnt_online];");
?8????????????????sb.Append("CREATE?TABLE?[dnt_online]?([olid]?[int]?IDENTITY?(1,?1)?NOT?NULL,[userid]?[int]?NOT?NULL,[ip]?[varchar]?(15)?NOT?NULL,[username]?[nvarchar]?(20)?NOT?NULL,[nickname]?[nvarchar]?(20)?NOT?NULL,[password]?[char]?(32)?NOT?NULL,[groupid]?[smallint]?NOT?NULL,[olimg]?[varchar]?(80)?NOT?NULL,[adminid]?[smallint]?NOT?NULL,[invisible]?[smallint]?NOT?NULL,[action]?[smallint]?NOT?NULL,[lastactivity]?[smallint]?NOT?NULL,[lastposttime]?[datetime]?NOT?NULL,[lastpostpmtime]?[datetime]?NOT?NULL,[lastsearchtime]?[datetime]?NOT?NULL,[lastupdatetime]?[datetime]?NOT?NULL,[forumid]?[int]?NOT?NULL,[forumname]?[nvarchar]?(50)?NOT?NULL,[titleid]?[int]?NOT?NULL,[title]?[nvarchar]?(80)?NOT?NULL,[verifycode]?[varchar]?(10)?NOT?NULL?)?ON?[PRIMARY];");
?9????????????????sb.Append("ALTER?TABLE?[dnt_online]?WITH?NOCHECK?ADD?CONSTRAINT?[PK_dnt_online]?PRIMARY?KEY?CLUSTERED?([olid])?ON?[PRIMARY];?");
10????????????????sb.Append("ALTER?TABLE?[dnt_online]?ADD?CONSTRAINT?[DF_dnt_online_userid]?DEFAULT?((-1))?FOR?[userid],CONSTRAINT?[DF_dnt_online_ip]?DEFAULT?('0.0.0.0')?FOR?[ip],CONSTRAINT?[DF_dnt_online_username]?DEFAULT?('')?FOR?[username],CONSTRAINT?[DF_dnt_online_nickname]?DEFAULT?('')?FOR?[nickname],CONSTRAINT?[DF_dnt_online_password]?DEFAULT?('')?FOR?[password],CONSTRAINT?[DF_dnt_online_groupid]?DEFAULT?(0)?FOR?[groupid],CONSTRAINT?[DF_dnt_online_olimg]?DEFAULT?('')?FOR?[olimg],CONSTRAINT?[DF_dnt_online_adminid]?DEFAULT?(0)?FOR?[adminid],CONSTRAINT?[DF_dnt_online_invisible]?DEFAULT?(0)?FOR?[invisible],CONSTRAINT?[DF_dnt_online_action]?DEFAULT?(0)?FOR?[action],CONSTRAINT?[DF_dnt_online_lastactivity]?DEFAULT?(0)?FOR?[lastactivity],CONSTRAINT?[DF_dnt_online_lastposttime]?DEFAULT?('1900-1-1?00:00:00')?FOR?[lastposttime],CONSTRAINT?[DF_dnt_online_lastpostpmtime]?DEFAULT?('1900-1-1?00:00:00')?FOR?[lastpostpmtime],CONSTRAINT?[DF_dnt_online_lastsearchtime]?DEFAULT?('1900-1-1?00:00:00')?FOR?[lastsearchtime],CONST??RAINT?[DF_dnt_online_lastupdatetime]?DEFAULT?(getdate())?FOR?[lastupdatetime],CONSTRAINT?[DF_dnt_online_forumid]?DEFAULT?(0)?FOR?[forumid],CONSTRAINT?[DF_dnt_online_forumname]?DEFAULT?('')?FOR?[forumname],CONSTRAINT?[DF_dnt_online_titleid]?DEFAULT?(0)?FOR?[titleid],CONSTRAINT?[DF_dnt_online_title]?DEFAULT?('')?FOR?[title],CONSTRAINT?[DF_dnt_online_verifycode]?DEFAULT?('')?FOR?[verifycode];");
11????????????????sb.Append("CREATE?INDEX?[forum]?ON?[dnt_online]([userid],?[forumid],?[invisible])?ON?[PRIMARY];");
12????????????????sb.Append("CREATE?INDEX?[invisible]?ON?[dnt_online]([userid],?[invisible])?ON?[PRIMARY];");
13????????????????sb.Append("CREATE?INDEX?[forumid]?ON?[dnt_online]([forumid])?ON?[PRIMARY];");
14????????????????sb.Append("CREATE?INDEX?[password]?ON?[dnt_online]([userid],?[password])?ON?[PRIMARY];");
15????????????????sb.Append("CREATE?INDEX?[ip]?ON?[dnt_online]([userid],?[ip])?ON?[PRIMARY];");
16
17????????????????return?DbHelper.ExecuteNonQuery(CommandType.Text,?sb.Replace("dnt_",?BaseConfigs.GetBaseConfig().Tableprefix).ToString());
18????????????}
19????????????catch
20????????????{
21????????????????return?-1;
22????????????}
23????????}
24
25
???? 該方法如果正常運行的話,會在數據庫中建立dnt_onlines"這個表。用于記錄在線用戶的全部
信息。
???? 上文介紹的僅僅是系統“初始化”時所要做的“活”。而更重要的內容是用戶從一訪問論壇并
執行一系列操作(如登陸,發貼,瀏覽版塊等)時,“在線用戶”機制在里面所啟的重要作用。
???? 這里先以登陸(系統)這一操作發生時,程序所做出的響應為例,看一下在線這塊是如何進行綁
定的,請看如下代碼(位于discuz.web項目的“aspx/1/”文件夾下的login.aspx.cs文件):
?????
????
2????OnlineUsers.UpdateAction(olid,?UserAction.Login.ActionID,?0,?config.Onlinetimeout);
3????
4
??? 它的作用就是將已成功登陸系統的用戶所執行的當前動作及相關信息更新到"dnt_onlines" 表
中。而這個函數本身所用到的四個參數要重點介紹一下:
???? olid: 在線列表id,對應"dnt_onlines"表中的olid字段
???? action:動作(結構類型,詳情參見discuz.forum項目下的forumutils.cs文件)
?????????? 用于傳遞動作的相關信息如:ActionName動作名稱和ActionDescription動作描述等)
???? inid: 所在位置代碼,即當前用戶所訪問的主題(TopicId)。因為登陸不牽扯主題操作,所
?????????? 以上面的值為0
???? timeout: 無動作離線時間(config.Onlinetimeout),這個數據是在后臺進行設置的,見下圖:
?
????? timeout的作用就是當有別人訪問論壇時,在更新自身在線狀態信息同時,用這個設置數據(
整型)與在線表(dnt_onlines)中的"lastupdatetime"字段(最后活動時間)進行比較,找出超過
timeout規定的時間的用戶,將其在線狀態(onlinestate)設置為0(即為離線)。而做這件事的函
數就是在項目“discuz.data.sqlserver”下的“UserManage.cs”中的“AddOnlineUser”函數,這
里將它的代碼貼出來,詳情見注釋:
????????
?1/**////?<summary>?2????????///?執行在線用戶向表及緩存中添加的操作。
?3????????///?</summary>
?4????????///?<param?name="__onlineuserinfo">在組用戶信息內容</param>
?5????????///?<returns>添加成功則返回剛剛添加的olid,失敗則返回0</returns>
?6????????public?int?AddOnlineUser(OnlineUserInfo?__onlineuserinfo,?int?timeout)
?7????????{
?8
?9????????????string?strDelTimeOutSql?=?"";
10????????????//?此處的設置見后臺forum_uisetting.aspx.cs源碼
11????????????//?如果timeout為負數則代表不需要精確更新用戶是否在線的狀態
12????????????if?(timeout?>?0)
13????????????{
14????????????????if?(__onlineuserinfo.Userid?>?0)
15????????????????{
16????????????????????strDelTimeOutSql?=?string.Format("{0}UPDATE?[{1}users]?SET?[onlinestate]=1?WHERE?[uid]={2};",?strDelTimeOutSql,?BaseConfigs.GetTablePrefix,?__onlineuserinfo.Userid.ToString());
17????????????????}
18????????????}
19????????????else
20????????????{
21????????????????timeout?=?timeout?*?-1;
22????????????}
23
24????????????if?(timeout?>?9999)
25????????????{
26????????????????timeout?=?9999;
27????????????}
28
29????????????System.Text.StringBuilder?sb?=?new?System.Text.StringBuilder();
30????????????System.Text.StringBuilder?sb2?=?new?System.Text.StringBuilder();
31
32????????????IDataReader?dr?=?DbHelper.ExecuteReader(CommandType.Text,?string.Format("SELECT?[userid]?FROM?[{0}online]?WHERE?[lastupdatetime]<'{1}'",?BaseConfigs.GetTablePrefix,?DateTime.Parse(DateTime.Now.AddMinutes(timeout?*?-1).ToString("yyyy-MM-dd?HH:mm:ss"))));
33????????????try
34????????????{
35????????????????while?(dr.Read())
36????????????????{
37????????????????????sb.Append(",");
38????????????????????sb.Append(dr[0].ToString());
39????????????????????if?(dr[0].ToString()?!=?"-1")
40????????????????????{
41????????????????????????sb2.Append(",");
42????????????????????????sb2.Append(dr[0].ToString());
43????????????????????}
44????????????????}
45????????????}
46????????????finally
47????????????{
48????????????????dr.Close();
49????????????}
50
51????????????if?(sb.Length?>?0)
52????????????{
53????????????????sb.Remove(0,?1);
54????????????????strDelTimeOutSql?=?string.Format("{0}DELETE?FROM?[{1}online]?WHERE?[userid]?IN?({2});",?strDelTimeOutSql,?BaseConfigs.GetTablePrefix,?sb.ToString());
55????????????}
56????????????if?(sb2.Length?>?0)
57????????????{
58????????????????sb2.Remove(0,?1);
59????????????????strDelTimeOutSql?=?string.Format("{0}UPDATE?[{1}users]?SET?[onlinestate]=0,[lastactivity]=GETDATE()?WHERE?[uid]?IN?({2});",?strDelTimeOutSql,?BaseConfigs.GetTablePrefix,?sb2.ToString());
60????????????}
61
62
63????????????DbParameter[]?prams?=?{};
64????????????int?olid?=?Utils.StrToInt(DbHelper.ExecuteScalar(CommandType.Text,?strDelTimeOutSql?+?"INSERT?INTO?["?+?BaseConfigs.GetTablePrefix?+?"online]?([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[forumname],[titleid],[title],?[verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT?SCOPE_IDENTITY()",?prams).ToString(),?0);
65
66????????????//?如果id值太大則重建在線表
67????????????if?(olid?>?2147483000)
68????????????{
69????????????????CreateOnlineTable();
70????????????????DbHelper.ExecuteNonQuery(CommandType.Text,?strDelTimeOutSql?+?"INSERT?INTO?["?+?BaseConfigs.GetTablePrefix?+?"online]?([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[titleid],[verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT?SCOPE_IDENTITY()",?prams);
71????????????????return?1;
72????????????}
73
74
75????????????return?0;
76????????????//return?(int)DbHelper.ExecuteDataset(CommandType.Text,?"SELECT?[olid]?FROM?["+BaseConfigFactory.GetTablePrefix+"online]?WHERE?[userid]="?+?__onlineuserinfo.Userid.ToString()).Tables[0].Rows[0][0];
77
78????????}
79
80
????? 需要說明一下,就是在線表的刪除問題。因為這個在線表是通過程序在數據庫中進行創建的,
同時表中的olid又是一個自增字段(標識自增為1),因此為了避免自增字段最終超過最大值范圍
(因為在線表經常有數據添加進來)。所以在上面加入了"olid > 2147483000"的邏輯判斷,來預
防這個問題的出現。
????? 說了一大堆的關于timeout參數的問題,而另一個重要的參數olid還沒作詳細介紹呢:)
???? 上面所述的登陸頁面(login.aspx.cs)中,olid是從basepage類中獲得的,而basepage就是前
臺主要頁面的“基類”,它里面的構造函數部分封裝了頁面中大部分公共變量的初始化操作。所以要
了解olid,還要從basepage.cs中分析一把。
??? 請看下面的代碼段(摘自discuz.web.ui項目下的basepage.cs文件):
????
?1/**////?<summary>?2????///?當前用戶的在線表ID
?3????///?</summary>
?4????protected?internal?int?olid;
?5
?6????public?BasePage()?//構造函數
?7????{
?8??????
?9??????oluserinfo?=?OnlineUsers.UpdateInfo(config.Passwordkey,?config.Onlinetimeout);
10??????
11??????//password?可用于下面的userkey賦值,以實現用戶退出操作時的識別認證
12??????password?=?oluserinfo.Password;??
13??????if?(password.Length?>?16)
14??????{
15??userkey?=?password.Substring(4,?8).Trim();
16??????}
17??????else
18??????{
19??userkey?=?"";
20??????}
21
22?????
23??????olid?=?oluserinfo.Olid;
24??????
25????}?
26
27
???? 從代碼中可以看出olid是通過louserinfo對象進行賦值的,而louserinfo對象是通過UpdateInfo
來進行實始化綁定的,所以我們還需要再看一下這個函數,相關代碼段如下:
???
?1??/**////?<summary>?2??///?用戶在線信息維護。判斷當前用戶的身份(會員還是游客),是否在在線列表中存在,如果存在則更新會員的當前動,不存在則建立.
?3??///?</summary>
?4??///?<param?name="passwordkey">論壇passwordkey</param>
?5??///?<param?name="timeout">在線超時時間</param>
?6??///?<param?name="passwd">用戶密碼</param>
?7??public?static?OnlineUserInfo?UpdateInfo(string?passwordkey,?int?timeout,?int?uid,?string?passwd)
?8??{
?9
10???lock(SynObject)
11???{
12????OnlineUserInfo?__onlineuser?=?new?OnlineUserInfo();
13???
14????string?ip?=?DNTRequest.GetIP();
15????int?userid?=?Utils.StrToInt(ForumUtils.GetCookie("userid"),?uid);
16????string?password?=?(passwd?==?string.Empty???ForumUtils.GetCookiePassword(passwordkey)?:?ForumUtils.GetCookiePassword(passwd,passwordkey));
17
18????if?(password.Length?==?0)
19????{
20?????userid?=?-1;
21????}
22?????//?如果密碼非Base64編碼字符串則懷疑被非法篡改,?直接置身份為游客
23????else?if?(!Utils.IsBase64String(password))
24????{
25?????userid?=?-1;
26????}
27
28????if?(userid?!=?-1)
29????{
30?????__onlineuser?=?GetOnlineUser(userid,password);
31?????if?(__onlineuser?!=?null)
32?????{
33??????
34??????if?(__onlineuser.Ip?!=?ip)
35??????{
36???????UpdateIP(__onlineuser.Olid,?ip);
37
38???????__onlineuser.Ip?=?ip;
39?????
40???????return?__onlineuser;
41??????}
42?????}
43?????else
44?????{
45??????
46??????//?判斷密碼是否正確
47??????userid?=?Users.CheckPassword(userid,?password,?false);
48??????if?(userid?!=?-1)
49??????{
50???????DeleteRowsByIP(ip);
51???????return?CreateUser(userid,?timeout);
52??????}
53??????else
54??????{
55???????//?如密碼錯誤則在在線表中創建游客
56???????__onlineuser?=?GetOnlineUserByIP(-1,?ip);
57???????if?(__onlineuser?==?null)
58???????{
59????????return?CreateGuestUser(timeout);
60???????}
61??????}
62?????}
63
64????}
65????else
66????{
67?????__onlineuser?=?GetOnlineUserByIP(-1,?ip);
68?????if?(__onlineuser?==?null)
69?????{
70??????return?CreateGuestUser(timeout);
71?????}
72
73????}
74
75????__onlineuser.Lastupdatetime?=?Utils.GetDateTime();
76????return?__onlineuser;
77??
78???}
79
80??}
81
82
????? 看到這里大家明白了吧,原來不管用戶是否注冊登陸,這個函數都會將一些信息更新或添加到在線
表中。即判斷當前用戶的身份(會員還是游客),是否在"在線列表"中存在,如果存在則更新會員的當前動作,
不存在(首次訪問)則初始化相關(游客)用戶信息。
????? 下面再介紹一下action這個參數,它是結構(struct)類型,里面的ActionName動作名稱和ActionD-
escription動作描述(目前未用上)是用于顯示當前動作信息的。相應的顯示效果如下圖所示:
?????
?????
?????
????? 如果大家覺得沒問題的話,下面再介紹一下注銷(退出)操作,因為有登陸就會有退出。而注銷這
塊的操作也很簡單,請看如下代碼段(位于aspx/1/logout.aspx.cs文件):
????
?2????{
?3????????protected?override?void?ShowPage()
?4????????{
?5????????????
?6
?7????????????if?(DNTRequest.GetString("userkey")?==?userkey)
?8????????????{
?9????????????????AddMsgLine("已經清除了您的登錄信息,?稍后您將以游客身份返回首頁");
10????????????????Users.UpdateOnlineTime(uid);
11????????????????OnlineUsers.DeleteRows(olid);
12????????????????ForumUtils.ClearUserCookie();
13????????????????Utils.WriteCookie(Utils.GetTemplateCookieName(),?"",?-999999);
14????????????}
15????????????else
16????????????{
17????????????????AddMsgLine("無法確定您的身份,?稍后返回首頁");
18????????????}
19????????}
20????}
21
22
????
??? 代碼中的userkey其實是一個用戶password的一個子串(詳情上面basepage.cs代碼中的相應部分)。
??? 而“OnlineUsers.DeleteRows(olid);”這行代碼其實就是刪除在線表中指定olid的用戶信息了。因
為代碼過于簡單,只是一條SQL語句而已,所以就不在這里多費筆默了。
??? 其實關于在線表有關的操作還有很多,大家不妨挖一下onlines.cs這個文件,從中會有更多的收獲。
??? 最后再介紹一下有關“最大在線人數”這個問題,其實這個功能主要是為了中小站長(服務器資源相
對緊張)提供的一項設置,旨在當用戶在線人數達到一定數量時,拒絕其它的訪問。相應的代碼判斷邏
輯如下(摘自basepage.cs文件):
???
?1?if?(config.Onlinetimeout?>?0?&&?userid?!=?-1)?2?{
?3??onlineusercount?=?OnlineUsers.GetOnlineAllUserCount();
?4?}
?5????else
?6?{
?7??onlineusercount?=?OnlineUsers.GetCacheOnlineAllUserCount();
?8?}
?9????if?(onlineusercount?>=?config.Maxonlines?&&?useradminid?!=?1?&&?pagename?!=?"login.aspx"?&&?pagename?!=?"logout.aspx")
10?{
11??ShowMessage("抱歉,目前訪問人數太多,你暫時無法訪問論壇.",?0);
12??return;
13?}
14
???? 里面的GetOnlineAllUserCount()和GetCacheOnlineAllUserCount()均為返回在線人數的方法,而
config.Onlinetimeout的設置是在管理后臺完成的,見下圖:
?
???? 好了,今天的文章就先寫到這里了,希望大家不要感覺是在“云里霧里”的:),如果有問題歡迎
與我交流或去我們的官方站點(http://nt.discuz.net)反映問題。
??? 我的email:? daizhj@discuz.com,? daizhj617595@126.com, daizhj@gmail.com
??? 關鍵字:.net,discuz,disucznt,online,在線,在線用戶
??? 作者:代震軍,daizhj
????
?
?
總結
以上是生活随笔為你收集整理的Discuz!NT 在线用户功能简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 过冰点自定义日期时间
- 下一篇: 一寸、两寸证件照photoshop批处理