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