任建華內(nèi)蒙古體育職業(yè)學(xué)院,內(nèi)蒙古呼和浩特 010051
Windows下大容量服務(wù)器軟件的開發(fā)與設(shè)計
任建華
內(nèi)蒙古體育職業(yè)學(xué)院,內(nèi)蒙古呼和浩特 010051
互聯(lián)網(wǎng)的普及使得同時訪問一個服務(wù)器的用戶越來越多,本文通過詳細(xì)對比幾種Winsock I/O方法的優(yōu)缺點,分析了在服務(wù)器開發(fā)中如何使用IOCP技術(shù)、重疊I/O技術(shù)、線程池技術(shù)、內(nèi)存池等技術(shù),并通過性能測試表明此服務(wù)器能夠處理海量套接字連接請求,可以使服務(wù)器具有極佳的性能和強大的擴展能力,為開發(fā)大容量、可伸縮性服務(wù)器打下了良好的基礎(chǔ)。
服務(wù)器軟件;完成端口;Winsock;多線程
服務(wù)器是指網(wǎng)絡(luò)中能對其他機器提供某些服務(wù)的計算機系統(tǒng)軟件或者設(shè)備。服務(wù)器作為網(wǎng)絡(luò)節(jié)點,存儲、處理網(wǎng)絡(luò)上80%的數(shù)據(jù)、信息,因此也被稱為網(wǎng)絡(luò)的靈魂。對于用戶來說,一臺計算機所能承受的負(fù)荷是多多益善的。微軟公司winsock2中引入了內(nèi)核級的完成端口(IOCP)模型,IOCP(I/O Completion Ports,I/O完成端口)技術(shù)是應(yīng)用程序使用線程池處理異步I/O請求的一種機制,作為Windows服務(wù)平臺上一種比較成熟的I/O處理技術(shù),完成端口模型可以利用為數(shù)不多的線程為成千上萬的套接字同時提供網(wǎng)絡(luò)服務(wù),隨著UNIX操作系統(tǒng)的廣泛應(yīng)用,套接字成為當(dāng)前最流行的網(wǎng)絡(luò)通信應(yīng)用程序接口之一。
完成端口技術(shù)是伸縮性最好的一種I/O模型,能隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多而線性的提升服務(wù)器的性能。把完成端口模型封裝成一個比較普通的C++類,只要繼承這個類,改寫其中的兩個虛函數(shù)(HandleData和DataAction)就可以滿足各種服務(wù)器的需要。通過用AcceptEx代替accept和使用LOOKASIDE LIST來管理內(nèi)存,服務(wù)器的性能有了較大的提升。
服務(wù)器主要分為2個模塊:主窗口模塊、服務(wù)器模塊。主窗口模塊負(fù)責(zé)初始化和啟動服務(wù)器模塊。服務(wù)器模塊為整個程序的核心,利用多個I/O工作線程在完成端口上處理來自眾多客戶端的異步I/O請求。
1.2.1 主窗口模塊
主窗口模塊主要用于初始化,同時負(fù)責(zé)啟動、設(shè)置服務(wù)器并設(shè)置IP地址和端口號。
1.2.2 服務(wù)器模塊
此模塊中,CompletionPortModel類是這個服務(wù)器的核心。這個類使用了IOCP技術(shù),它可以非常高效的為大量客戶提供服務(wù)。CompletionPortModel類有多個I/O工作線程在完成端口上處理異步I/O調(diào)用,當(dāng)網(wǎng)絡(luò)事件發(fā)生時,這些線程調(diào)用類中的虛函數(shù)(HandleData和DataAction)來為事件服務(wù)。CompletionPortModel類接收到啟動命令后,首先創(chuàng)建監(jiān)聽線程,再由監(jiān)聽線程創(chuàng)建I/O工作線程。服務(wù)器啟動期間,監(jiān)聽線程一直運行,為I/O工作線程提供服務(wù)。
1 )完成端口類的聲明
2 )開始服務(wù)
成員函數(shù)Init用于初始化,創(chuàng)建完成端口、創(chuàng)建完成端口處理線程,首先調(diào)用類成員函數(shù)InitWinsock初始化Winsock、建立一個監(jiān)聽套接字m_ListenSocket,并將此套接字同完成端口關(guān)聯(lián)起來,獲取AcceptEx指針。其次再調(diào)用BindAndListenSocket()將監(jiān)聽套接字m_ListenSocket綁定到主窗口模塊輸入的本地IP地址和端口。并置于監(jiān)聽模式。
3 )內(nèi)存管理
(1)使用旁視列表管理內(nèi)存
每當(dāng)有一個新連接到來的時候,服務(wù)器程序都要為該程序創(chuàng)建一個結(jié)構(gòu)體(單句柄數(shù)據(jù))用于存儲該客戶端的socket信息,而在每一次的WSASend和WSARecv中,工作線程也要向系統(tǒng)投遞一個重疊類型的結(jié)構(gòu)體(單IO數(shù)據(jù))用于數(shù)據(jù)的收發(fā)。為了高效的分配和釋放數(shù)據(jù),采用Lookaside List(旁視列表)來管理內(nèi)存的分配與回收。
(2)內(nèi)存資源異常處理
應(yīng)用服務(wù)器管理眾多客戶端I/O請求時,數(shù)據(jù)緩沖區(qū)很可能會被鎖定。操作系統(tǒng)對鎖定內(nèi)存的數(shù)量有限制,達(dá)到這個限制時,重疊操作就會以WSAENOBUFS錯誤失敗。實際設(shè)計中,當(dāng)服務(wù)器處理大量并發(fā)客戶端時,可以在每個連接上投遞一個0字節(jié)的接收操作,這樣就不會有內(nèi)存被鎖定。0字節(jié)的接收操作完成以后,服務(wù)器可以執(zhí)行一個非阻塞的接收來獲取緩沖區(qū)中的所有數(shù)據(jù)。
4 )連接管理
(1)使用AcceptEx管理客戶端連接
服務(wù)器端程序在接受客戶端連接的時候通常調(diào)用Accept函數(shù),該函數(shù)是一個阻塞函數(shù),當(dāng)該Accept函數(shù)沒有返回時又有新客戶端連接請求已經(jīng)發(fā)過來時,新客戶端就要處于等待。在本程序中,把FD_ACCPET事件和一個Event關(guān)聯(lián)起來,然后用WaitForSingleObject等待這個Event,當(dāng)已經(jīng)發(fā)出的AcceptEx調(diào)用數(shù)目耗盡而又有新的客戶端需要連接時,F(xiàn)D_ACCEPT事件將被觸發(fā),Event變?yōu)橐褌餍艩顟B(tài),WaitForSingleObject返回,然后調(diào)用成員函數(shù)PostAccetpEx()重新發(fā)出10個AcceptEx調(diào)用。
(2)惡意客戶端連接問題
惡意的客戶連接是指客戶端出現(xiàn)壞鏈接,有的終端既不發(fā)送數(shù)據(jù),也不關(guān)閉連接,就會造成AcceptEx投遞的大量重疊操作不能返回,為了滿足其他客戶端請求,服務(wù)器不得不再繼續(xù)投遞更多的接收I/O,占用了大量的系統(tǒng)資源,為了避免這個事件發(fā)生,應(yīng)用服務(wù)器記錄了所有AcceptEx投遞的未決I/O請求,在現(xiàn)場中定時遍歷它們,對每個客戶端套接字以SO_CONNECT_TIME為參數(shù)調(diào)用getsockop函數(shù),檢查套接字建立的時間,如果某個連接沒有按時收發(fā)過數(shù)據(jù),則通過使用PostQueuedCompletionStatus()函數(shù)強制關(guān)閉該套接字發(fā)送強制關(guān)閉。
5 )數(shù)據(jù)處理
(1)基本的數(shù)據(jù)處理
為了測試服務(wù)器能否響應(yīng)大數(shù)量的客戶端連接,設(shè)計了回應(yīng)服務(wù)器,通過自定義枚舉型數(shù)據(jù)結(jié)構(gòu),用來標(biāo)識套接字的I/O操作類型,對應(yīng)單IO數(shù)據(jù)結(jié)構(gòu)中的一個參數(shù),這樣HandleData()和DataAction()即可根據(jù)詳細(xì)的操作類型來進行下一步的IO操作。當(dāng)服務(wù)器端完成了用WSARecv數(shù)據(jù)接收完畢時,就調(diào)用WSASend把剛收到的數(shù)據(jù)重新發(fā)送給客戶端(即將IO操作標(biāo)志設(shè)為IoWrite);如果上一次服務(wù)器端完成了WSASend將數(shù)據(jù)發(fā)送完成時,就將后續(xù)的操作標(biāo)志設(shè)為關(guān)閉(IoEnd)。
(2)包重新排序問題
使用I/O完成端口的操作會按照它們被提交的順序完成,但是線程調(diào)度可能會影響最終結(jié)果。本文采用向提交的緩沖區(qū)對象中添加序列號的方法來解決上面的問題,如果緩沖區(qū)序列號是連續(xù)的,就處理緩沖區(qū)中的數(shù)據(jù),因此有錯誤序列號的緩沖區(qū)要被保存下來,以便今后使用。
在本機(Intel Core2 Duo CPU T7500 2.2GHz 2GB內(nèi)存)上同時運行了服務(wù)器和虛擬客戶端。測試時,虛擬客戶端分別啟動了100,、200、300、400、500、600、700、800、900、1000、1100、1200、1300、1400、1500、1600、1700、1800、1900、2000個線程,代表不同數(shù)量的客戶端同時訪問服務(wù)器。
圖2 服務(wù)器測試結(jié)果
從圖2可以看出,隨著連接客戶數(shù)量的增多,服務(wù)器對CPU的占用率并沒有急速增長。而是緩慢增長,這是服務(wù)器伸縮性好的一種表現(xiàn)。另外,服務(wù)器對內(nèi)存的消耗量也是穩(wěn)定的呈線性緩慢增長,說明服務(wù)器在內(nèi)存消耗上是高效的,穩(wěn)定的。本文給出的設(shè)計實例,已在上調(diào)試通過,該方法簡單實用,為分析提供了參考資料,有助于進一步研究以為內(nèi)核的具有豐富硬件資源的操作系統(tǒng)的移植。
[1]Anthony Jones,Windows網(wǎng)絡(luò)編程技術(shù)[M].北京:機械工業(yè)出版社,2000,89-242.
[2]王艷平,張越.Windows網(wǎng)絡(luò)與通信程序設(shè)計.北京:人民郵電出版社,2006:13-99.
[3]馬金鑫,袁丁.基于IOCP的高并發(fā)通信服務(wù)器的設(shè)計與實現(xiàn)[J].通信技術(shù),2009(7):248-250.
[4]吳星,黃愛萍.用完成端口實現(xiàn)可擴展的服務(wù)器應(yīng)用[期刊論文].計算機科學(xué).2002,29(11):144-164.
TP31
A
1674-6708(2012)59-0129-02