IOCP局域網(wǎng)監(jiān)控研究論文

時間:2022-03-12 09:51:00

導語:IOCP局域網(wǎng)監(jiān)控研究論文一文來源于網(wǎng)友上傳,不代表本站觀點,若需要原創(chuàng)文章可咨詢客服老師,歡迎參考。

IOCP局域網(wǎng)監(jiān)控研究論文

摘要本文介紹了一種在WINDOWS平臺上比較成熟的I/O方法---完成端口,提出了通過使用iocp機制和旁視列表技術(shù)建立網(wǎng)絡(luò)服務(wù)器模型的方法,實現(xiàn)了利用為數(shù)不多的線程為成千上萬的客戶同時提供網(wǎng)絡(luò)服務(wù),解決了大多數(shù)網(wǎng)絡(luò)服務(wù)器連接大量客戶端和處理大量數(shù)據(jù)時存在的問題,獲得了極好的性能和強大的擴展能力。文章給出了基于Windows2000平臺的局域網(wǎng)監(jiān)控系統(tǒng)中網(wǎng)絡(luò)服務(wù)器的設(shè)計與實現(xiàn)過程。

關(guān)鍵詞完成端口;旁視列表;網(wǎng)絡(luò)服務(wù)器;VC++

1引言

在局域網(wǎng)遠程監(jiān)控系統(tǒng)中,網(wǎng)絡(luò)管理中心需要同時監(jiān)控每個客戶端計算機的運行情況,將遠程監(jiān)控畫面顯示在管理中心的電腦屏幕上。網(wǎng)絡(luò)服務(wù)器要求實現(xiàn)定時截獲客戶端計算機上的屏幕數(shù)據(jù)與正在運行程序的數(shù)據(jù),定時截取客戶端屏幕圖像,并將截獲的數(shù)據(jù)和圖像保存在數(shù)據(jù)庫中等功能。在較大型局域網(wǎng)中有幾百上千個客戶端,需要對大量的客戶端數(shù)據(jù)進行處理。因此如何讓網(wǎng)絡(luò)服務(wù)器同時為多個客戶端服務(wù),但又不喪失整體的性能成為開發(fā)的難點。本文采用了I/O完成端口、旁視列表等技術(shù),設(shè)計了一種高效的網(wǎng)絡(luò)服務(wù)器,讓此問題得到了有效的解決。

2IOCP機制

IOCP(I/OCompletionPort輸入/輸出完成端口)是一種能夠合理利用與管理多線程的機制。該機制使用完成端口,用一定數(shù)量的線程處理重疊I/O(OverlappedI/O)的技術(shù),幫助處理大量客戶端請求的網(wǎng)絡(luò)服務(wù)問題,特別適合于開發(fā)網(wǎng)絡(luò)服務(wù)器一類的應(yīng)用程序,并可使系統(tǒng)的性能達到較佳狀態(tài)。IOCP模型圖如圖1所示。

圖1完成端口工作模式

完成端口模式要求創(chuàng)建一個Win32完成端口對象來對重疊I/O請求進行管理,并通過創(chuàng)建一定數(shù)量的工作者線程(WorkThread),來為已經(jīng)完成的重疊I/O請求提供服務(wù)。其實,可以把完成端口看成系統(tǒng)維護的一個隊列,操作系統(tǒng)把重疊I/O操作完成的事件通知放入該隊列,由于是“操作完成”的事件通知,故取名為“完成端口”。一個完成端口被創(chuàng)建以后,可以和多個文件句柄進行關(guān)聯(lián)(文件句柄可以是真正的文件句柄,也可以是Socket句柄或命名管道),并在關(guān)聯(lián)后的句柄上進行重疊I/O操作。當I/O操作完成后,一個重疊I/O完成的事件通知就會被排在此端口的完成隊列上,此時,某個工作者線程將會被喚醒來為完成端口服務(wù),執(zhí)行特定的處理工作。一般來說,一個應(yīng)用程序可以創(chuàng)建多個工作者線程來處理完成端口上的通知事件,工作者線程的數(shù)量依賴于程序的具體需要。

3數(shù)據(jù)庫同步機制實現(xiàn)過程:

(系統(tǒng)結(jié)構(gòu)圖如圖2)

圖2系統(tǒng)結(jié)構(gòu)圖

(1)整體系統(tǒng)安裝初始,各個同步端的數(shù)據(jù)庫初始化為一致。

(2)數(shù)據(jù)服務(wù)器維護各個同步端的SQL隊列,不斷檢查是否有操作項,然后將各項操作依序發(fā)送至各個在線的同步端。

(3)在線同步端收到后,置入SQL隊列,將SQL隊列按操作發(fā)生時間先后排序

(4)在線同步端將SQL隊列中的各項操作依序執(zhí)行并通知服務(wù)器執(zhí)行的結(jié)果。服務(wù)器收到后將該項操作從該同步端的SQL隊列中刪除。

(5)某一在線同步端執(zhí)行非讀的SQL操作后,向服務(wù)器發(fā)送該操作,服務(wù)器將該操作放入除了此同步端外的其他同步端的SQL隊列中,并放入歷史SQL文件,然后轉(zhuǎn)到第2步

(6)有新同步端注冊入系統(tǒng)后,將從數(shù)據(jù)服務(wù)器(數(shù)據(jù)服務(wù)器同時建立該同步端的SQL隊列)下載歷史SQL文件,并執(zhí)行其中各項操作。

(7)某同步端選擇注銷,數(shù)據(jù)服務(wù)器刪除該同步端的SQL隊列。

4內(nèi)存分配機制

在該系統(tǒng)中內(nèi)存的分配和釋放比較頻繁,為了比較高效分配釋放數(shù)據(jù),采用一種稱作LookasideList(旁視列表)的數(shù)據(jù)結(jié)構(gòu)。在該系統(tǒng)中用于單句柄數(shù)據(jù)和重疊操作數(shù)據(jù)(完成鍵)的分配和釋放。LookasideList的原理是在分配一塊數(shù)據(jù)空間前,先查看回收鏈表中是否有數(shù)據(jù)空間指針,如果有則不需要調(diào)用系統(tǒng)的內(nèi)存分配,直接使用回收鏈表中的該指針指向的數(shù)據(jù)空間,并將回收鏈表中該指針節(jié)點移除,當使用完數(shù)據(jù)空間后,也不必調(diào)系統(tǒng)的內(nèi)存釋放,而將該數(shù)據(jù)空間指針移入回收鏈表。在進程最終結(jié)束后才將回收鏈表中的所有指針指向的數(shù)據(jù)空間釋放。從而大大減低程序在運行期的頻繁內(nèi)存分配和釋放產(chǎn)生的開銷。

5實現(xiàn)過程

5.1初始化

讀取服務(wù)器配置文件,初始化客戶端連接鏈表;建立完成端口,根據(jù)CPU個數(shù)建立等待和接受完成通知的線程;初始化WinSock接口,建立偵聽SOCKET;取擴展函數(shù)AcceptEx;關(guān)聯(lián)完成端口到偵聽SOCKET;綁定和偵聽。

5.2啟動服務(wù)

(1)建立網(wǎng)絡(luò)事件、設(shè)置該網(wǎng)絡(luò)事件為偵聽SOCKET的ACCEPT網(wǎng)絡(luò)事件,這樣當AcceptEx一次性分配的N個預(yù)備SOCKET由于滿足不斷上來的TCP連接而耗盡,并有新的TCP連接請求時,就會觸發(fā)偵聽SOCKET的ACCEPT事件,服務(wù)器就會在這個時機調(diào)用AcceptEx再次分配N個新的預(yù)備SOCKET以滿足新的TCP連接請求。在該系統(tǒng)中,N=10。

(2)分配的N個預(yù)備SOCKET

(3)建立檢查和分配線程,該線程作用:等待ACCEPT網(wǎng)絡(luò)事件以分配新的預(yù)備SOCKET;定時檢測和掛斷未收發(fā)任何數(shù)據(jù)且超時連接的TCP連接,防止DoS(拒絕服務(wù))攻擊。

5.3完成通知線程內(nèi)部處理

while(TRUE)

{

//等待完成端口的通知

bSuccess=GetQueuedCompletionStatus(

pThis->m_hCOP,

&dwNumberBytes,

(PULONG_PTR)&lpHandleContext,

&lpOverlapped,

INFINITE

);

if(!bSuccess)

{

pThis->m_strLog.Format("GetQueuedCompletionStatus()失敗:%d",GetLastError());

Log(pThis->m_strLog);

PPER_IO_CONTEXTlpPerIoContext=(PPER_IO_CONTEXT)lpOverlapped;

lpPerIoContext->pNext=NULL;

pThis->InsertToLookaside(lpPerIoContext,NULL);

lpHandleContext->pNext=NULL;

pThis->InsertToLookaside(NULL,lpHandleContext);

continue;

}

//如果外部發(fā)送空的單句柄數(shù)據(jù)指針,則退出完成通知線程

if(NULL==lpHandleContext)

{

return0;

}

//將完成鍵轉(zhuǎn)換為自己格式的數(shù)據(jù)指針

PPER_IO_CONTEXTlpPerIoContext=(PPER_IO_CONTEXT)lpOverlapped;

//這種情況表示客戶端自己掛斷連接

if(IoAccept!=lpPerIoContext->IoOperation)

{

if(0==dwNumberBytes)

{

shutdown(lpPerIoContext->sClient,SD_BOTH);

closesocket(lpPerIoContext->sClient);

lpPerIoContext->pNext=NULL;

pThis->InsertToLookaside(lpPerIoContext,NULL);

lpHandleContext->pNext=NULL;

pThis->InsertToLookaside(NULL,lpHandleContext);

pThis->DecreaseClientNum();

continue;

}

}

HANDLEhResult;

PPER_HANDLE_CONTEXTlpNewperHandleContext;

//判斷該通知為哪種類型的操作的結(jié)果

switch(lpPerIoContext->IoOperation)

{

//接受新的TCP連接

caseIoAccept:

pThis->IncreaseClientNum();

EnterCriticalSection(&pThis->m_ListCriSection);

pThis->ReleaseConnectionNode

(lpPerIoContext);

LeaveCriticalSection(&pThis->m_ListCriSection);

//將偵聽SOCKET的屬性復制給客戶端SOCKET,因為accept創(chuàng)建的socket會自動繼承偵聽socket的屬性,而AcceptEx不會

nResult=setsockopt(

lpPerIoContext->sClient,

SOL_SOCKET,

SO_UPDATE_ACCEPT_CONTEXT,

(char*)&pThis->m_ListenSocket,

sizeof(pThis->m_ListenSocket)

);

if(SOCKET_ERROR==nResult)

{

pThis->m_strLog.Format("客戶(ID=%d)SO_UPDATE_ACCEPT_CONTEXT失敗:%d",

lpPerIoContext->unId,WSAGetLastError());

Log(pThis->m_strLog);

closesocket(lpPerIoContext->sClient);

lpPerIoContext->pNext=NULL;

pThis->InsertToLookaside(lpPerIoContext,NULL);

pThis->DecreaseClientNum();

continue;

}

//為新的SOCKET分配單句柄數(shù)據(jù)

lpNewperHandleContext=pThis->GetHandleFromLookaside();

if(NULL==lpNewperHandleContext)

{

lpNewperHandleContext=(PPER_HANDLE_CONTEXT)HeapAlloc(

GetProcessHeap(),

HEAP_ZERO_MEMORY,

sizeof(PER_HANDLE_CONTEXT)

);

if(NULL==lpNewperHandleContext)

{

pThis->m_strLog.Format("HeapAlloc()失敗");

Log(pThis->m_strLog);

shutdown(lpPerIoContext->sClient,SD_BOTH);

closesocket(lpPerIoContext->sClient);

lpPerIoContext->pNext=NULL;

pThis->InsertToLookaside(lpPerIoContext,NULL);

pThis->DecreaseClientNum();

continue;

}

}

//將新的SOCKET關(guān)聯(lián)到同一完成端口上

lpNewperHandleContext->IoSocket=lpPerIoContext->sClient;

lpNewperHandleContext->pNext=NULL;

hResult=CreateIoCompletionPort(

(HANDLE)lpPerIoContext->sClient,\

pThis->m_hCOP,

(DWORD_PTR)lpNewperHandleContext,

);

if(NULL==hResult)

{

pThis->m_strLog.Format("關(guān)聯(lián)完成端口到客戶套接字失敗:%d",GetLastError());

Log(pThis->m_strLog);

shutdown(lpPerIoContext->sClient,SD_BOTH);

closesocket(lpPerIoContext->sClient);

lpPerIoContext->pNext=NULL;

lpNewperHandleContext->pNext=NULL;

pThis->InsertToLookaside(lpPerIoContext,NULL);

pThis->InsertToLookaside(NULL,lpNewperHandleContext);

pThis->DecreaseClientNum();

continue;

}

//接收來自CLIENT的數(shù)據(jù)

if(pThis->HandleData(lpPerIoContext,IO_READ_COMPLETION,dwNumberBytes))

pThis->DataAction(lpPerIoContext,lpNewperHandleContext);

continue;

//數(shù)據(jù)接收完的通知

caseIoRead:

if(pThis->HandleData(lpPerIoContext,IO_READ_COMPLETION,dwNumberBytes))

pThis->DataAction(lpPerIoContext,lpNewperHandleContext);

continue;

//數(shù)據(jù)發(fā)送完的通知

caseIoWrite:

if(pThis->HandleData(lpPerIoContext,IO_WRITE_COMPLETION,dwNumberBytes))

pThis->DataAction(lpPerIoContext,lpNewperHandleContext);

continue;

default:

continue;

}

}

數(shù)據(jù)處理都放在HandleData中實現(xiàn),數(shù)據(jù)處理完后的響應(yīng)代碼都在DataAction中實現(xiàn),在此不做詳細介紹。

6結(jié)束語

編寫網(wǎng)絡(luò)服務(wù)器應(yīng)用程序的難點在于程序的“可擴展性”,即如何開發(fā)出大容量且能處理大量并發(fā)SocketI/O請求的高性能應(yīng)用程序。IOCP機制通過完成端口對象來對重疊I/O請求進行管理,并且利用多線程來處理重疊I/O操作完成后得到的數(shù)據(jù),是一種與Win32Sockets結(jié)合度較高的實現(xiàn)高效率I/O的有效方法。

本文設(shè)計的網(wǎng)絡(luò)服務(wù)器在局域網(wǎng)監(jiān)控系統(tǒng)中成功使用。實際應(yīng)用表明:利用IOCP機制實現(xiàn)的網(wǎng)絡(luò)服務(wù)器應(yīng)用程序能夠針對大量的客戶請求進行高效處理,在速度和性能上體現(xiàn)出其良好的特征,不失為一種實現(xiàn)網(wǎng)絡(luò)服務(wù)器的有效技術(shù)。

參考文獻

[1]張靜華,張玉明.IOCP研究及在大規(guī)模網(wǎng)絡(luò)通信系統(tǒng)中的應(yīng)用.計算機與現(xiàn)代化,2004,9

[2]陳和平,周靜寧等.IOCP機制與網(wǎng)絡(luò)服務(wù)器實現(xiàn)方法.計算機應(yīng)用,2003,4

[3]AnthonyJones,JimOhlund著.Windows網(wǎng)絡(luò)編程技術(shù).機械工業(yè)出版社,2000,(176-206)

[4]潘愛民,王國印譯.VisualC++技術(shù)內(nèi)幕(第四版).清華大學出版社,1999

[5]官章全,韓云君等.VisualC++6.0高級編程范例.電子工業(yè)出版社,2001