四.Windows I/O模型之重叠IO(overlapped)模型
1.適用于除Windows CE之外的各種Windows平臺.在使用這個模型之前應該確保該系統安裝了Winsock2.重疊模型的基本設計原理是使用一個重疊的數據結構,一次投遞一個或多個Winsock I/O請求。在重疊模型中,收發數據使用WSA開頭的函數。
2.WSA_FLAG_OVERLAPPED標志:要使用重疊模型。在創建套接字的時候,必須加上該標志。
SOCKET s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
假如使用的是socket函數,而非WSASocket函數,那么會默認設置WSA_FLAG_OVERLAPPED標志。
若隨一個WSAFLAGOVERLAPPED結構一起調用這些以WSA開頭的函數(AcceptEx和TRansmiteFile函數例外),函數會立即完成并返回,無論套接字是否設為阻塞模式
3.重疊模型在網絡事件完成后,可以有兩種方式通知應用程序:事件通知和完成例程
3.事件通知:事件對象與WSAOVERLAPPED進行綁定實現網絡事件完成后通過事件進行通知。
4.WSAOVERLAPPED結構:
typedef struct WSAOverlapped
{
??? DWORD Internal;
??? DOWRD InternalHigh;
??? DWORD Offset;
??? DWORD OffsetHigh;
??? WSAEVENT hEvent;
}WSAOVERLAPPED,FAR* LPWSAOVERLAPPED;
此處,程序員可以使用的是最后一個參數hEvent,其余的不用管。通過該參數,重疊結構可以與事件對象進行綁定,以實現網絡事件完成后,通過事件對象進行通知應用程序。事件對象通知方式是通過創建一個事件對象,把該對象賦值給重疊結構的hEvent參數即可實現綁定。在此再次提醒大家注意:WSAWaitForMultipleEvents函數一次最多只能等待64個事件對象。
5.WSAGetOverlappedResult函數:重疊請求完成后,接著需要調用WSAGetOverlappedResult(取得重疊結構)函數,判斷那個重疊調用到底是成功,還是失敗.
BOOL WSAGetOverlappedResult(
??? SOCKET s,//套接字
??? LPWSAOVERLAPPED lpOverlapped,//重疊結構
??? LPDWORD lpcbTransfer,//對應一個DWORD(雙字節)變量,一次重疊實際傳輸(接收或者發送)的字節數
??? BOOL fWait,//參數用于決定函數是否應該等待一次重疊操作完成。
??? LPWORD lpdwFlags
??? );
重疊操作完成,函數返回TRUE,否則,返回FALSE。返回FALSE通常都是有一下幾種情況造成的.
(1)重疊I/O操作仍處在未完成狀態
(2)重疊操作已經完成,但含有錯誤
(3)重疊操作的完成狀態不可判決,因為在提供給函數WSAGetOverlappedResult的一個或多個參數中,存在著錯誤。
失敗后,由lpcbTransfer參數指向的值不會進行更新,而且我們的應用程序應調用WSAGetLastError函數
6.基于事件通知的重疊模型編程步驟如下:
(1) 創建一個套接字,綁定本機端口,在指定的端口上監聽連接請求。
(2)接受連接請求。
(3)為接受的套接字新建一個WSAOVERLAPPED結構,并為該結構分配一個事件對象句柄。也將事件對象句柄分配給一個事件數組,以便稍后由函數WSAWaitForMultipleEvents使用。
(4)在套接字上投遞一個異步WSARecv請求,指定參數為WSAOVERLAPPED結構。注意函數通常會以失敗告終,返回SOCKETERROR錯誤狀態WSAIOPENDING(I/O操作尚未完成)。
(5)使用步驟3)的事件數組,調用WSAWaitForMultipleEvents函數,并等待與重疊調用關聯在一起的事件進入“已傳信”狀態(換言之,等待那個事件的“觸發”)。
(6)WSAWaitForMultipleEvents函數完成后,針對事件數組,調用WSAResetEvent(重設事件)函數,從而重設事件對象,并對完成的重疊請求進行處理。
(7)使用WSAGetOverlappedResult函數,判斷重疊調用的返回狀態是什么。
(8)在套接字上投遞另一個重疊WSARecv請求。
(9)重復步驟5 ) ~ 8 )。
示例代碼:
注意:對于接受客戶端連接的函數,還有一個AcceptEx函數,不過這個函數太過麻煩且對性能的提升沒有太大的作用,暫時不打算學習
?
接下來學習基于完成例程的重疊IO模型:
7.基于完成例程的重疊模型的實現:
完成例程也就是回調函數。這種方式,就是設置一個回調函數,I/O請求完成后,自動調用回調函數即可。如果希望用完成例程為重疊I/O請求提供服務,在我們的應用程序中,必須為指定一個完成例程(回調函數),同時指定一個WSAOVERLAPPED結構。一個完成例程(回調函數)必須擁有下述函數原型:
8.基于完成例程和基于事件對象兩種重疊模型之間的差異:在使用完成重疊模型時,WSAOVERLAPPED結構的事件字段hEvent并未使用;也就是說,我們不可將一個事件對象同重疊請求關聯到一起。用完成例程發出了一個重疊I/O請求之后,作為我們的調用線程,一旦完成,它最終必須為完成例程提供服務。這樣一來,便要求我們將自己的調用線程置于一種“可警告的等待狀態”。并在I/O操作完成后,對完成例程加以處理。WSAWaitForMultipleEvents函數可用來將我們的線程置于一種可警告的等待狀態。這樣做的缺點在于,我們還必須有一個事件對象可用于WSAWaitForMultipleEvents函數。假定應用程序只用完成例程對重疊請求進行處理,便不可能有任何事件對象需要處理。作為一種變通方法,我們的應用程序可用Win32的SleepEx函數將自己的線程置為一種可警告等待狀態。當然,亦可創建一個偽事件對象,不將它與任何東西關聯在一起。假如調用線程經常處于繁忙狀態,而且并不處在一種可警告的等待狀態,那么根本不會有投遞的完成例
程會得到調用。
SleepEx函數的行為實際上和WSAWaitForMultipleEvents差不多,只是它不需要任何事件對象。
9.SleepEx函數:
第二個參數是否置于警覺狀態,如果為FALSE,則一定要等待超時時間完畢之后才會返回,這里我們是希望重疊操作一完成就能返回,所以我們要設置為TRUE
10.window下,管理重疊io有三種方式:基于事件的重疊io模型,基于完成例程的重疊io模型,完成端口模型,相比之下,在性能方面前兩種方式沒有區別,第三種完成端口模型性能最優。因為前兩種都需要自己來管理任務的分派,而完成端口是操作系統來管理任務的分派。雖然第一二中Io模型在性能上沒有區別。但是,基于事件的重疊io模型要受到最多64個等待事件的限制。完成例程則沒有這種限制。
11.完成歷程的基本原理:在基于事件的重疊io模型中,io操作完成后,系統會以事件的方式通知應用程序。而對于完成例程來說,系統會在io操作完成后,直接調用應用程序提供的回調函數。區別也就僅此而已。對于完成例程,我們可以用一種非常形象的話來進行表述:完成例程的處理過程,也就像我們告訴系統,說“我想要在網絡上接收網絡數據,你去幫我辦一下”(投遞WSARecv操作),“不過我并不知道網絡數據合適到達,總之在接收到網絡數據之后,你直接就調用我給你的這個函數(比如_CompletionProess),把他們保存到內存中或是顯示到界面中等等,全權交給你處理了”,于是乎,系統在接收到網絡數據之后,一方面系統會給我們一個通知,另外同時系統也會自動調用我們事先準備好的回調函數,就不需要我們自己操心了。
12.完成例程的函數基本上和基于事件的io模型的函數是相同的。只是在這里我們需要添加一個回調函數。回調函數原型如下:(函數名字隨便起,但是參數類型不能錯):
13.定義了回調函數之后,還需要把回調函數與系統綁定之后,網絡操作完成后才會自動調用回調函數。例如WSARecv綁定過程如下:
1 int WSARecv( 2 SOCKET s, // 當然是投遞這個操作的套接字 3 LPWSABUF lpBuffers, // 接收緩沖區,與Recv函數不同,這里需要一個由WSABUF結構構成的數組 4 DWORD dwBufferCount, // 數組中WSABUF結構的數量,設置為1即可 5 LPDWORD lpNumberOfBytesRecvd,//所接收到的字節數 6 LPDWORD lpFlags, // 說來話長了,我們這里設置為0 即可 7 LPWSAOVERLAPPED lpOverlapped, // “綁定”的重疊結構 8 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //回調函數的指針 9 ); View Code??? //注意:綁定動作就是通過填充最后一個參數進行實現的。
14.完成例程的實現步驟:
(1)創建一個套接字,開始在指定的端口上監聽連接請求
(2)接收連接請求即調用accept函數,生成一個新的socket套接字
(3)準備重疊結構:需要注意的是,一個套接字對應一個或者多個io請求,一個io請求對應一個重疊結構
??? WSAOVERLAPPED AcceptOverlapped; ?
??? ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));????? // 置零
(4)投遞io請求(此處為WSARecv請求),使用重疊結構,回調函數填充參數:
??? WSARecv(AcceptSocket,&DataBuf,1,&dwRecvBytes, &Flags,&AcceptOverlapped,_CompletionRoutine)
(5)重疊操作綁定完成后,接下來就是等待io請求的完成結果。有兩種方式可以實現:調用WSAWaitForMultipleEvents函數或者SleepEx函數。
(6)通過等待函數的返回值取得io請求的結果:在等待函數中,正常情況下,在操作完成之后,應該是返回WAIT_IO_COMPLETION,如果返回的是 WAIT_TIMEOUT,則表示等待設置的超時時間到了,但是重疊操作依舊沒有完成,應該通過循環再繼續等待。如果是其他返回值,那就壞事了,說明網絡通信出現了其他異常,程序就可以報錯退出了
1 if(dwIndex == WAIT_IO_COMPLETION) 2 { 3 TRACE("重疊操作完成.../n"); 4 } 5 else if( dwIndex==WAIT_TIMEOUT ) 6 { 7 TRACE(“超時了,繼續調用等待函數”); 8 } 9 else 10 { 11 TRACE(“廢了…”); 12 } View Code(7)投遞下一個io請求,重復4-7
15.讀取io請求的最終結果數據:在WSARecv調用的時候,是傳遞了一個WSABUF的變量的,用于保存網絡數據,而在我們寫的完成例程回調函數里面,就可以取到客戶端傳送來的網絡數據了。因為系統在調用我們完成例程函數的時候,其實網絡操作已經完成了,WSABUF里面已經有我們需要的數據了,只是通過完成例程來進行后期的處理
示例代碼:
?
轉載于:https://www.cnblogs.com/HPAHPA/p/7819498.html
總結
以上是生活随笔為你收集整理的四.Windows I/O模型之重叠IO(overlapped)模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php之快速入门学习-9(switch)
- 下一篇: autoitv3点击windows界面