本發(fā)明涉及網(wǎng)絡(luò)通信技術(shù)領(lǐng)域,尤其是涉及一種基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng)及方法。
背景技術(shù):
目前實(shí)現(xiàn)在線客戶端人數(shù),最高訪問用戶數(shù)的統(tǒng)計(jì)的技術(shù)方案主要是針對基于HTTP協(xié)議的網(wǎng)站或者服務(wù)器的訪問,比較成熟的方案是用Servlet規(guī)范中定義的事件監(jiān)聽器(Listener)來實(shí)現(xiàn),但是只能進(jìn)行粗略的統(tǒng)計(jì),難以做到精確,比如,用戶沒有退出登錄而直接關(guān)閉了瀏覽器,那么在服務(wù)器端的Session中,這個(gè)用戶仍然是存在的,造成統(tǒng)計(jì)的誤差‘
在物聯(lián)網(wǎng)領(lǐng)域,大部分的穿戴式設(shè)備以及智能手機(jī)設(shè)備與服務(wù)器的連接往往是基于TCP協(xié)議的,針對linux系統(tǒng)而言,可以通過相關(guān)的指令查看服務(wù)器某個(gè)端口的TCP連接數(shù)來估算在線用戶的數(shù)量,比如,統(tǒng)計(jì)80端口連接數(shù)可以采用以下指令,netstat-nat|grep-i"80"|wc–l,但是此方法也只能粗略的估計(jì),而且只是知道數(shù)量,至于是什么用戶在線,什么時(shí)候上線卻沒法獲取,而且前面已經(jīng)跟服務(wù)器建立連接的客戶端,理論上連接可以一直長期維持下去,但實(shí)際情況并非如此,比如為了避免不必要的帶寬資源浪費(fèi),網(wǎng)絡(luò)運(yùn)營商會(huì)將采用
GPRS通信的建立鏈接后一定時(shí)間內(nèi)不進(jìn)行數(shù)據(jù)傳輸?shù)目蛻舳俗詣?dòng)斷掉,實(shí)際上TCP斷開的因素比較多,除非是服務(wù)器或客戶端主動(dòng)發(fā)起斷開請求,否則對于因斷電、網(wǎng)線拔掉、網(wǎng)絡(luò)癱瘓、強(qiáng)行關(guān)閉客戶端軟件等原因造成的異常斷開,服務(wù)器未必能夠及時(shí)有效的監(jiān)測到TCP連接的斷開,如果還采用指令查看系統(tǒng)的具體端口的TCP的連接數(shù)的方法統(tǒng)計(jì)在線數(shù)量,顯然統(tǒng)計(jì)出的在線客戶端數(shù)量是不準(zhǔn)確的,因?yàn)槭孪瓤蛻舳伺c服務(wù)器建立連接的TCP即便異常斷開了,但在服務(wù)器一直存在,實(shí)際上建立的是“死連接”,用戶無法能繼續(xù)和服務(wù)器進(jìn)行通信。
技術(shù)實(shí)現(xiàn)要素:
有鑒如此,有必要針對現(xiàn)在技術(shù)存在的缺陷,提供一種可以通過動(dòng)態(tài)鏈表的方式統(tǒng)計(jì)在線客戶端數(shù)量的系統(tǒng)。
為實(shí)現(xiàn)上述目的,本發(fā)明采用下述技術(shù)方案:
一種基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng),包括:服務(wù)器及與所述服務(wù)器TCP連接的至少一客戶端,所述服務(wù)器用于創(chuàng)建動(dòng)態(tài)鏈表,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,每個(gè)結(jié)點(diǎn)包括:客戶端數(shù)據(jù)域和存儲下一個(gè)結(jié)點(diǎn)地址的指針域next;
所述服務(wù)器通過調(diào)用epoll_wait函數(shù),判斷所述客戶端是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā);
其中,所述通信事件包括新的客戶端發(fā)起TCP連接請求事件、已經(jīng)建立連接的客戶端斷開TCP連接的事件、已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录?、已?jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录⒓靶奶O(jiān)測的定時(shí)器中斷服務(wù)事件。
在一些實(shí)施例中,每個(gè)結(jié)點(diǎn)的長度等于當(dāng)前與所述服務(wù)器建立TCP連接的客戶端數(shù)量。
在一些實(shí)施例中,所述客戶端數(shù)據(jù)域包括:存儲所述客戶端ID的字符數(shù)組類型變量id,存儲所述服務(wù)器給當(dāng)前上線客戶端分配的socket文件描述符的整型變量fd,用于所述服務(wù)器心跳包丟失統(tǒng)計(jì)的整型變量heartbeat_lost_count。
另外,本申請還提供了一種基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)方法,包括下述步驟:
步驟S110:對所述服務(wù)器Socket通信參數(shù)進(jìn)行初始化;
步驟S120:所述服務(wù)器建立并監(jiān)聽socket套接字描述符socket_fd;
步驟S130:對所述服務(wù)器的模型epoll進(jìn)行初始化;
步驟S140:所述服務(wù)器創(chuàng)建動(dòng)態(tài)鏈表并啟動(dòng)控制心跳包的系統(tǒng)定時(shí)器;
步驟S150:所述服務(wù)器調(diào)用epoll_wait函數(shù),并判斷所述客戶端是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),所述服務(wù)器遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā);
其中,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,每個(gè)結(jié)點(diǎn)包括:客戶端數(shù)據(jù)域和存儲下一個(gè)結(jié)點(diǎn)地址的指針域next;
其中,所述通信事件包括新的客戶端發(fā)起TCP連接請求事件、已經(jīng)建立連接的客戶端斷開TCP連接的事件、已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录?、已?jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录?、及心跳包監(jiān)測的定時(shí)器中斷服務(wù)事件。
在一些實(shí)施例中,在步驟S150中,所述服務(wù)器遍歷并處理所述通信事件,當(dāng)所述通信事件為新的客戶端發(fā)起TCP連接請求命令,具體包括下述步驟:
epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件剛好等于監(jiān)聽套接字描述符socket_fd;
根據(jù)socket_fd調(diào)用accept函數(shù)返回新的socket套接字描述符,記為fd;
將偵聽到的fd通過epoll_ctl函數(shù)添加到epoll句柄當(dāng)中。
在一些實(shí)施例中,在步驟S150中,所述服務(wù)器遍歷并處理所述通信事件,當(dāng)所述通信事件為已經(jīng)建立連接的客戶端斷開TCP連接的事件,具體包括下述步驟:
epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
根據(jù)fd調(diào)用read函數(shù);
判斷read函數(shù)返回值是否等于0,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
根據(jù)發(fā)起斷開請求的客戶端的fd查找結(jié)點(diǎn)所在位置,并將結(jié)點(diǎn)從所述動(dòng)態(tài)鏈表中刪除。
在一些實(shí)施例中,在步驟S150中,所述服務(wù)器遍歷并處理所述通信事件,當(dāng)所述通信事件為已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录唧w包括下述步驟:
epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
根據(jù)fd調(diào)用read函數(shù);
判斷read函數(shù)返回值是否大于0,若是,進(jìn)行下一步;若否,進(jìn)行其他處理;
判斷數(shù)據(jù)包是否為HEL;xxxxxxxxxxx;@的格式,其中,HEL是hello的縮寫,xxxxxxxxxxx是所述客戶端的ID,用分號";"隔開,最后以@結(jié)束,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
對所述xxxxxxxxxxx和分配該客戶端的fd進(jìn)行提取,以開辟新結(jié)點(diǎn),并將所述xxxxxxxxxxx和fd分別存儲到新結(jié)點(diǎn)的id和fd中,且將heartbeat_lost_count初始化為0;
將所述新節(jié)點(diǎn)添加到所述動(dòng)態(tài)鏈表的結(jié)尾。
在一些實(shí)施例中,在步驟S150中,所述服務(wù)器遍歷并處理所述通信事件,當(dāng)所述通信事件為已經(jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录唧w包括下述步驟:
epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
根據(jù)fd調(diào)用read函數(shù);
判斷read函數(shù)返回值是否大于0,若是,進(jìn)行下一步;若否,進(jìn)行其他處理;
判斷數(shù)據(jù)包是否為HEART;xxxxxxxxxxx;@的格式,其中,xxxxxxxxxxx是所述客戶端的ID,用分號";"隔開,最后以@結(jié)束,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
根據(jù)客戶端的ID查找客戶端所在的動(dòng)態(tài)鏈表中的結(jié)點(diǎn)位置;
將查到的和該客戶端對應(yīng)結(jié)點(diǎn)中的heartbeat_lost_count清零。
在一些實(shí)施例中,在步驟S150中,所述服務(wù)器遍歷并處理所述通信事件,當(dāng)所述通信事件為心跳包監(jiān)測的定時(shí)器中斷服務(wù)事件,具體包括下述步驟:
判斷當(dāng)前遍歷到的結(jié)點(diǎn)地址是否等于NULL;若是進(jìn)下一步;
給所述動(dòng)態(tài)鏈表中當(dāng)前遍歷到結(jié)點(diǎn)的heartbeat_lost_count按1步進(jìn)遞增;
判斷heartbeat_lost_count是否大于5,若是,進(jìn)行下一步;若否,遍歷下一個(gè)結(jié)點(diǎn);
判定為超時(shí)未發(fā)送心跳包,根據(jù)客戶端的fd查找結(jié)點(diǎn)所在位置并將結(jié)點(diǎn)從動(dòng)態(tài)鏈表中刪除;
遍歷下一個(gè)結(jié)點(diǎn),并返回第一步。
在一些實(shí)施例中,每個(gè)結(jié)點(diǎn)的長度等于當(dāng)前與所述服務(wù)器建立TCP連接的客戶端數(shù)量,所述客戶端數(shù)據(jù)域包括:存儲所述客戶端ID的字符數(shù)組類型變量id,存儲所述服務(wù)器給當(dāng)前上線客戶端分配的socket文件描述符的整型變量fd,用于所述服務(wù)器心跳包丟失統(tǒng)計(jì)的整型變量heartbeat_lost_count。
本發(fā)明采用上述技術(shù)方案的優(yōu)點(diǎn)是:
本發(fā)明提供的基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng),包括:服務(wù)器及與所述服務(wù)器TCP連接的至少一客戶端,所述服務(wù)器用于創(chuàng)建動(dòng)態(tài)鏈表,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,所述服務(wù)器通過調(diào)用epoll_wait函數(shù),判斷所述客戶端是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā),本發(fā)明提供的技術(shù)方案利用epoll服務(wù)器通信模型,提出了通過動(dòng)態(tài)鏈表的方式統(tǒng)計(jì)在線客戶端的數(shù)量;本發(fā)明還提出了增強(qiáng)統(tǒng)計(jì)準(zhǔn)確性的機(jī)制,給每一個(gè)客戶端提供有條不紊的心跳包控制機(jī)制,實(shí)時(shí)監(jiān)測和處理異常斷開的客戶端,從而增強(qiáng)在線客戶端數(shù)量統(tǒng)計(jì)方法的穩(wěn)定性可靠性以及準(zhǔn)確性。
附圖說明
圖1為本發(fā)明實(shí)施例提供的基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng)的結(jié)構(gòu)示意圖。
圖2為本申請一較佳實(shí)施例提供的動(dòng)態(tài)鏈接的結(jié)構(gòu)示意圖。
圖3為本申請?zhí)峁┑幕赥CP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)方法的步驟流程圖。
圖4為本申請?zhí)峁┑男碌目蛻舳税l(fā)起TCP連接請求的事件處理的步驟流程圖。
圖5為本申請?zhí)峁┑囊呀?jīng)建立連接的客戶端斷開TCP連接的事件的步驟流程圖。
圖6表示為已經(jīng)建立連接的客戶端(結(jié)點(diǎn)2)發(fā)起斷開請求的動(dòng)態(tài)鏈表。
圖7表示為將發(fā)起斷開請求的客戶端(對應(yīng)結(jié)點(diǎn)2)從鏈表中刪除并重構(gòu)動(dòng)態(tài)鏈表。
圖8為本申請?zhí)峁┑囊呀?jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录奶幚砹鞒虉D。
圖9表示當(dāng)前在線的客戶端動(dòng)態(tài)鏈表(數(shù)量為2)。
圖10表示有新的客戶端上線后的動(dòng)態(tài)鏈表(數(shù)量為3)。
圖11為本申請?zhí)峁┑囊呀?jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录鞒虉D。
圖12為本申請?zhí)峁┑男奶O(jiān)測的定時(shí)器中斷服務(wù)事件流程圖。
圖13為服務(wù)器壓力測試(模擬)示意圖。
圖14為模擬客戶端的TCP/IP調(diào)試工具。
圖15為上線的客戶端在前端web頁面的顯示示意圖。
圖16為服務(wù)器監(jiān)視界面示意圖。
具體實(shí)施方式
請參閱圖1,本發(fā)明實(shí)施例提供的一種基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng),包括:服務(wù)器110及與所述服務(wù)器110TCP連接的至少一客戶端120。
所述服務(wù)器110用于創(chuàng)建動(dòng)態(tài)鏈表,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,每個(gè)結(jié)點(diǎn)包括:客戶端數(shù)據(jù)域和存儲下一個(gè)結(jié)點(diǎn)地址的指針域next。
優(yōu)選地,該動(dòng)態(tài)鏈表的結(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)id_fd_node定義如下:
typedefstructid_fd_str
{
char id[13];//存儲客戶端ID的字符數(shù)組
intfd;//存儲服務(wù)器分配給已經(jīng)建立連接的客戶端的套接字描述符
intheartbeat_lost_count;//心跳包丟失統(tǒng)計(jì)相關(guān)變量
structid_fd_str*next;//存儲指向下一個(gè)結(jié)點(diǎn)的地址
}id_fd_node;
請參閱圖2,為本申請一較佳實(shí)施例提供的動(dòng)態(tài)鏈接的結(jié)構(gòu)示意圖,該鏈表包括一個(gè)頭結(jié)點(diǎn)和若干結(jié)點(diǎn),每一個(gè)結(jié)點(diǎn)包括兩部分:客戶端數(shù)據(jù)域和存儲下一個(gè)結(jié)點(diǎn)地址的指針域next,每個(gè)結(jié)點(diǎn)通過指針域next連接起來構(gòu)成一個(gè)動(dòng)態(tài)鏈表,頭結(jié)點(diǎn)指向第一個(gè)結(jié)點(diǎn),第一個(gè)結(jié)點(diǎn)指向第二個(gè)結(jié)點(diǎn),依次類推,最后一個(gè)結(jié)點(diǎn)不再指向其他結(jié)點(diǎn),因此指針域用”null”填充。結(jié)點(diǎn)的長度等于當(dāng)前與服務(wù)器建立TCP連接的客戶端數(shù)量(也就是在線客戶端的數(shù)量),客戶端數(shù)據(jù)域包括三部分:存儲客戶端ID的字符數(shù)組類型變量id,存儲服務(wù)器給當(dāng)前上線客戶端分配的socket文件描述符的整型變量fd,用于服務(wù)器心跳包丟失統(tǒng)計(jì)的整型變量heartbeat_lost_count。
所述服務(wù)器110通過調(diào)用epoll_wait函數(shù),判斷所述客戶端120是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā);
其中,所述通信事件包括新的客戶端發(fā)起TCP連接請求事件、已經(jīng)建立連接的客戶端斷開TCP連接的事件、已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录?、已?jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录⒓靶奶O(jiān)測的定時(shí)器中斷服務(wù)事件。
可以理解,對于服務(wù)器110而言,客戶端120每次上線系統(tǒng)分配的fd都是隨機(jī)的,具有不確定性,但是在系統(tǒng)中每一個(gè)客戶端的ID都是固定的(用11位手機(jī)號碼表示),本發(fā)明中利用鏈表的結(jié)點(diǎn)可以完美的將fd和用戶的ID動(dòng)態(tài)綁定起來,建立映射關(guān)系,只要知道ID就可以通過鏈表查找獲取對應(yīng)的fd,反之亦可。
請參閱圖3,本申請?zhí)峁┑幕赥CP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)方法的步驟流程圖,包括下述步驟:
步驟S110:對所述服務(wù)器Socket通信參數(shù)進(jìn)行初始化;
步驟S120:所述服務(wù)器建立并監(jiān)聽socket套接字描述符socket_fd;
步驟S130:對所述服務(wù)器的模型epoll進(jìn)行初始化;
步驟S140:所述服務(wù)器創(chuàng)建動(dòng)態(tài)鏈表并啟動(dòng)控制心跳包的系統(tǒng)定時(shí)器;
步驟S150:所述服務(wù)器調(diào)用epoll_wait函數(shù),并判斷所述客戶端是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),所述服務(wù)器遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā);
其中,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,每個(gè)結(jié)點(diǎn)包括:客戶端數(shù)據(jù)域和存儲下一個(gè)結(jié)點(diǎn)地址的指針域next。
具體地,本申請?zhí)峁┑姆?wù)器110是運(yùn)行在linux系統(tǒng)下,基于epoll通信模型,epoll是為處理大批量句柄而作了改進(jìn)的poll。epoll具有以下優(yōu)點(diǎn),支持一個(gè)進(jìn)程打開大數(shù)目的socket描述符(FD);IO效率不隨FD數(shù)目增加而線性下降;使用mmap加速內(nèi)核與用戶空間的消息傳遞,理論上,1GB內(nèi)存的linux操作系統(tǒng)機(jī)器,如果采用epoll服務(wù)器模型,大約可以支持10萬個(gè)的客戶端TCP/IP連接。
可以理解,epoll只有epoll_create,epoll_ctl,epoll_wait 3個(gè)系統(tǒng)調(diào)用,使用非常簡單,服務(wù)器110啟動(dòng)后,首先要進(jìn)行一系列的初始化包括服務(wù)器Socket通信參數(shù)初始化,建立監(jiān)聽socket套接字描述符socket_fd,然后對服務(wù)器通信模型epoll初始化,再創(chuàng)建只包含頭結(jié)點(diǎn)的鏈表和初始化并啟動(dòng)控制心跳包的系統(tǒng)定時(shí)器,最后調(diào)用epoll_wait函數(shù),采用阻塞的方式等待客戶端通信事件的觸發(fā),只有滿足以下事件之一,服務(wù)器主程序才會(huì)退出epoll_wait函數(shù),對客戶端進(jìn)行觸發(fā)響應(yīng),客戶端上線,下線,客戶端之間的通信,在線客戶端數(shù)量的變化,針對服務(wù)器而言,主要?dú)w結(jié)為以下的客戶端的通信事件的發(fā)生和處理。
1、新的客戶端發(fā)起TCP連接請求。
2、已經(jīng)建立連接的客戶端斷開TCP連接的事件。
3、已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录?/p>
4、已經(jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录?/p>
5、心跳包監(jiān)測的定時(shí)器中斷服務(wù)事件。
以下結(jié)合具體實(shí)施例分別對上述5種情況進(jìn)行詳細(xì)說明。
實(shí)施例1-新的客戶端發(fā)起TCP連接請求
請參閱圖4,為本申請?zhí)峁┑男碌目蛻舳税l(fā)起TCP連接請求的事件處理的步驟流程圖,包括下述步驟:
S11:epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件剛好等于監(jiān)聽套接字描述符socket_fd;
S12:根據(jù)socket_fd調(diào)用accept函數(shù)返回新的socket套接字描述符,記為fd;
S13:將偵聽到的fd通過epoll_ctl函數(shù)添加到epoll句柄當(dāng)中。
可以理解,該客戶端事件的發(fā)生并不馬上完成客戶端的上線工作(需要下文的上線命令),只是需要客戶端先發(fā)起TCP連接請求,才能獲取服務(wù)器分配給它的fd,因?yàn)橐呀?jīng)建立連接的客戶端必須通過fd才能與服務(wù)器進(jìn)行數(shù)據(jù)的傳輸,包括向服務(wù)器發(fā)送上線命令也是要知道客戶端的fd。
實(shí)施例2-已經(jīng)建立連接的客戶端斷開TCP連接的事件
請參閱圖5,為本申請?zhí)峁┑囊呀?jīng)建立連接的客戶端斷開TCP連接的事件的步驟流程圖,包括下述步驟:
步驟S21:epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
步驟S22:根據(jù)fd調(diào)用read函數(shù);
步驟S23:判斷read函數(shù)返回值是否等于0,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
步驟S24:根據(jù)發(fā)起斷開請求的客戶端的fd查找結(jié)點(diǎn)所在位置,并將結(jié)點(diǎn)從所述動(dòng)態(tài)鏈表中刪除。
可以理解,如果已經(jīng)建立連接的客戶端斷開TCP連接,那么read函數(shù)的返回值為0,服務(wù)器會(huì)根據(jù)發(fā)起斷開請求的客戶端的fd查找結(jié)點(diǎn)所在鏈表中的位置并將結(jié)點(diǎn)從鏈表中刪除掉。然后將該刪除的結(jié)點(diǎn)的前后結(jié)點(diǎn)通過結(jié)點(diǎn)的指針域next連接起來構(gòu)成新的鏈表,由此可見,該事件會(huì)使得在線客戶端數(shù)量減少,而實(shí)際的在線客戶端數(shù)量可以通過計(jì)算當(dāng)前動(dòng)態(tài)鏈表的長度(除了頭結(jié)點(diǎn))而得。
可以理解,通過上述操作后,動(dòng)態(tài)鏈表的結(jié)構(gòu)和長度將發(fā)生變化,請參閱如圖6及圖7,分別表示已經(jīng)建立連接的客戶端(結(jié)點(diǎn)2)發(fā)起斷開請求及將發(fā)起斷開請求的客戶端(對應(yīng)結(jié)點(diǎn)2)從鏈表中刪除并重構(gòu)動(dòng)態(tài)鏈表。
實(shí)施例3-已經(jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录?/p>
請參閱圖8,為本申請?zhí)峁┑囊呀?jīng)建立TCP連接的客戶端有上線命令傳輸?shù)氖录奶幚砹鞒虉D,包括下述步驟:
步驟S31:epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
步驟S32:根據(jù)fd調(diào)用read函數(shù);
步驟S33:判斷read返回值是否大于0,若是,進(jìn)行下一步;若否,進(jìn)行其他處理;
步驟S34:判斷數(shù)據(jù)包是否為HEL;xxxxxxxxxxx;@的格式,其中,HEL是hello的縮寫,xxxxxxxxxxx是所述客戶端的ID,用分號";"隔開,最后以@結(jié)束,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
步驟S35:對所述xxxxxxxxxxx和分配該客戶端的fd進(jìn)行提取,以開辟新結(jié)點(diǎn),并將所述xxxxxxxxxxx和fd分別存儲到新結(jié)點(diǎn)的id和fd中,且將heartbeat_lost_count初始化為0;
步驟S36:將所述新節(jié)點(diǎn)添加到所述動(dòng)態(tài)鏈表的結(jié)尾。
可以理解,在服務(wù)器與客戶端之間進(jìn)行正常的數(shù)據(jù)收發(fā)之前,首先客戶端根據(jù)服務(wù)器端的IP和端口號發(fā)起TCP連接請求,服務(wù)器監(jiān)聽到有新客戶端連接后,在系統(tǒng)中會(huì)分配一個(gè)fd,接下來客戶端還要按照下述格式來給服務(wù)器發(fā)送上線通知的命令才能算真正的上線,格式如下:HEL;xxxxxxxxxxx;@(HEL是hello的縮寫,意思是向服務(wù)器打招呼,xxxxxxxxxxx是客戶端的ID(手機(jī)號),用分號";"隔開,最后以@結(jié)束)
從圖8中可以看出,服務(wù)器收到“HEL;xxxxxxxxxxx;@”的命令后,將會(huì)對xxxxxxxxxxx和分配給該客戶端的fd進(jìn)行提取,并將它們分別存儲到結(jié)點(diǎn)的id和fd當(dāng)中以便將fd和id建立映射關(guān)系,并對結(jié)點(diǎn)中的heartbeat_lost_count初始化為0,并將該結(jié)點(diǎn)添加到鏈表尾巴當(dāng)中,至此,客戶端完成了與服務(wù)器連接并且上線的操作,那么動(dòng)態(tài)鏈表的結(jié)構(gòu)和長度將發(fā)生變化(如圖6,圖7所示)。如果有新的客戶端發(fā)起上線操作,將重復(fù)剛才的流程,并在鏈表的結(jié)尾添加新的結(jié)點(diǎn)。那么通過計(jì)算鏈表的長度(除了頭結(jié)點(diǎn))就可以獲得當(dāng)前在線客戶端的人數(shù)。
可以理解,通過上述操作后,動(dòng)態(tài)鏈表的結(jié)構(gòu)和長度將發(fā)生變化,請參閱如圖9及圖10,分別表示當(dāng)前在線的客戶端動(dòng)態(tài)鏈表(數(shù)量為2)及有新的客戶端上線后的動(dòng)態(tài)鏈表(數(shù)量為3)。
實(shí)施例4-已經(jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录?/p>
請參閱圖11,為本申請?zhí)峁┑囊呀?jīng)建立TCP連接的客戶端有心跳包命令傳輸?shù)氖录鞒虉D,包括下述步驟:
步驟S41:epoll_wait函數(shù)發(fā)現(xiàn)觸發(fā)待處理的事件為socket套接字描述符,記為fd;
步驟S42:根據(jù)fd調(diào)用read函數(shù);
步驟S43:判斷read函數(shù)返回值是否大于0,若是,進(jìn)行下一步;若否,進(jìn)行其他處理;
步驟S44:判斷數(shù)據(jù)包是否為HEART;xxxxxxxxxxx;@的格式,其中,xxxxxxxxxxx是所述客戶端的ID,用分號";"隔開,最后以@結(jié)束,若是,進(jìn)行下一步;若否,進(jìn)行其他事務(wù)處理;
步驟S45:根據(jù)客戶端的ID查找客戶端所在的動(dòng)態(tài)鏈表中的結(jié)點(diǎn)位置;
步驟S46:將查到的和該客戶端對應(yīng)結(jié)點(diǎn)中的heartbeat_lost_count清零。
可以理解,客戶端上線后,需要周期性的給服務(wù)器發(fā)送固定格式和長度的數(shù)據(jù),也就是心跳包。格式為:HEART;xxxxxxxxxxx;@,其中HEART為心跳包命令同步頭,xxxxxxxxxxx為客戶端的ID號。如圖11所示,服務(wù)器每收到HEART;xxxxxxxxxxx;@后,立刻給客戶端返回xxxxxxxxxxx(也就是客戶端的ID號),然后根據(jù)客戶端的ID號查找該客戶端所在鏈表當(dāng)中的結(jié)點(diǎn)位置并將heartbeat_lost_count清零,在發(fā)心跳包之前務(wù)必先發(fā)上線命令:HEL;xxxxxxxxxxx;@,否則服務(wù)器會(huì)返回錯(cuò)誤信息:"sendheartbeart fail!,the client(ID:xxxxxxxxxxx;)is off line,please send HEL command first\n"。
實(shí)施例5-心跳包監(jiān)測的定時(shí)器中斷服務(wù)事件
請參閱圖12,為本申請?zhí)峁┑男奶O(jiān)測的定時(shí)器中斷服務(wù)事件流程圖,包括下述步驟:
步驟S51:判斷當(dāng)前遍歷到的結(jié)點(diǎn)地址是否等于NULL;若是進(jìn)下一步;
步驟S52:給所述動(dòng)態(tài)鏈表中當(dāng)前遍歷到結(jié)點(diǎn)的heartbeat_lost_count按1步進(jìn)遞增;
步驟S53:判斷heartbeat_lost_count是否大于5,若是,進(jìn)行下一步;若否,遍歷下一個(gè)結(jié)點(diǎn);
步驟S54:判定為超時(shí)未發(fā)送心跳包,根據(jù)客戶端的fd查找結(jié)點(diǎn)所在位置并將結(jié)點(diǎn)從動(dòng)態(tài)鏈表中刪除;
步驟S55:遍歷下一個(gè)結(jié)點(diǎn),并返回第一步。
可以理解,服務(wù)器進(jìn)行初始化后,會(huì)啟動(dòng)一個(gè)定時(shí)器中斷服務(wù)函數(shù),如圖8所示,等時(shí)間間隔觸發(fā)一次。在定時(shí)器中斷服務(wù)函數(shù)中,對鏈表進(jìn)行遍歷,給每一個(gè)結(jié)點(diǎn)的heartbeat_lost_count按照1的步進(jìn)遞增,然后對每一個(gè)結(jié)點(diǎn)的heartbeat_lost_count的值進(jìn)行判斷,如果heartbeat_lost_count大于預(yù)設(shè)值,那么服務(wù)器認(rèn)為該客戶端丟失心跳包的時(shí)間大于預(yù)設(shè)值(流程圖中設(shè)為5),服務(wù)器將根據(jù)客戶端的fd查找客戶端所在鏈表的結(jié)點(diǎn)位置,強(qiáng)制將客戶端所在的結(jié)點(diǎn)刪除掉,那么動(dòng)態(tài)鏈表的結(jié)構(gòu)和長度將發(fā)生變化(請參考圖9,圖10的說明)。并給客戶端返回"connection time out!,please online again\r\n"的錯(cuò)誤信息,然后斷開與該客戶端的TCP連接從而強(qiáng)制讓其下線,此時(shí)統(tǒng)計(jì)的在線客戶端數(shù)量也會(huì)減少,從而統(tǒng)計(jì)的在線客戶端的數(shù)量更加的準(zhǔn)確。
正常情況下,因?yàn)榉?wù)器會(huì)按照客戶端發(fā)心跳包的頻率給客戶端回應(yīng)心跳包(也就是客戶端的ID號),并將heartbeat_lost_count清零,因此定時(shí)器中斷服務(wù)函數(shù)中的heartbeat_lost_count是不會(huì)超過預(yù)設(shè)值的,只有因?yàn)榫W(wǎng)絡(luò)原因服務(wù)器接收不到客戶端的心跳包命令從而不能及時(shí)給heartbeat_lost_count清零,導(dǎo)致在定時(shí)器中斷服務(wù)函數(shù)中不斷的對heartbeat_lost_count遞增才會(huì)超過預(yù)設(shè)值。
請參閱圖13及14,為本發(fā)明通過TCP/IP壓力測試工具及模擬客戶端的TCP/IP調(diào)試工具來測試服務(wù)器的客戶端連接數(shù)和數(shù)據(jù)吞吐性能,目前已經(jīng)開發(fā)了基于Android系統(tǒng)的客戶端以及基于epoll通信服務(wù)器的監(jiān)控系統(tǒng),服務(wù)器能夠按照前面所述流程處理,上線信息顯示在前端web網(wǎng)頁當(dāng)中如圖15,服務(wù)器運(yùn)行于阿里云端,性能穩(wěn)定,通信可靠如圖16。
結(jié)合實(shí)施例1、實(shí)施例2、實(shí)施例3、實(shí)施例4及實(shí)施例5,可以理解,服務(wù)器維護(hù)著一個(gè)動(dòng)態(tài)鏈表,鏈表的長度可彈性縮放,長度取決與當(dāng)前在線(登陸)的客戶端數(shù)量,每一個(gè)結(jié)點(diǎn)代表著一個(gè)客戶端,一旦有新的客戶端發(fā)起TCP連接請求并發(fā)送了上線命令,就將其信息填充到新的結(jié)點(diǎn)并插入原鏈表的尾部,一旦已經(jīng)建立TCP連接的客戶端發(fā)起斷開請求(包括主動(dòng)發(fā)起或者異常斷開的情況),服務(wù)器就將其從鏈表結(jié)構(gòu)當(dāng)中剔除掉。由于鏈表的結(jié)點(diǎn)是根據(jù)需要?jiǎng)討B(tài)開辟的,因此相比需要實(shí)現(xiàn)定義固定長度的數(shù)組結(jié)構(gòu)而言,極大的提高內(nèi)存使用效率,由于是通過指針域操作鏈表的結(jié)點(diǎn),因此訪問效率極高。滿足了服務(wù)器對于海量客戶端并發(fā)連接和并發(fā)通信的高速要求。
可以理解,客戶端每次上線系統(tǒng)分配的fd都是隨機(jī)的,具有不確定性,但是在系統(tǒng)中每一個(gè)客戶端的ID都是固定的,本系統(tǒng)通過上線命令和心態(tài)包命令,結(jié)合動(dòng)態(tài)鏈表的建立和操作,可以完美的將fd和用戶的ID動(dòng)態(tài)綁定起來,無論客戶端在什么終端登錄,服務(wù)器都能夠識別。
本發(fā)明提供的基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng),包括:服務(wù)器及與所述服務(wù)器TCP連接的至少一客戶端,所述服務(wù)器用于創(chuàng)建動(dòng)態(tài)鏈表,所述動(dòng)態(tài)鏈表包括一頭結(jié)點(diǎn)和若干子結(jié)點(diǎn),以所述頭結(jié)點(diǎn)為起始,每個(gè)結(jié)點(diǎn)通過指針域next依次串聯(lián)構(gòu)成所述動(dòng)態(tài)鏈表,所述服務(wù)器通過調(diào)用epoll_wait函數(shù),判斷所述客戶端是否有通信事件觸發(fā),若是,退出epoll_wait函數(shù),遍歷并處理所述通信事件;若否,采用阻塞的方式等待客戶端通信事件的觸發(fā),本發(fā)明提供的技術(shù)方案利用epoll服務(wù)器通信模型,提出了通過動(dòng)態(tài)鏈表的方式統(tǒng)計(jì)在線客戶端的數(shù)量;本發(fā)明還提出了增強(qiáng)統(tǒng)計(jì)準(zhǔn)確性的機(jī)制,給每一個(gè)客戶端提供有條不紊的心跳包控制機(jī)制,實(shí)時(shí)監(jiān)測和處理異常斷開的客戶端,從而增強(qiáng)在線客戶端數(shù)量統(tǒng)計(jì)方法的穩(wěn)定性可靠性以及準(zhǔn)確性。
當(dāng)然本發(fā)明的基于TCP/IP通信協(xié)議的在線客戶端數(shù)量的統(tǒng)計(jì)系統(tǒng)及方法還可具有多種變換及改型,并不局限于上述實(shí)施方式的具體結(jié)構(gòu)??傊景l(fā)明的保護(hù)范圍應(yīng)包括那些對于本領(lǐng)域普通技術(shù)人員來說顯而易見的變換或替代以及改型。