日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

手把手教你玩转SOCKET模型:重叠I/O篇

發(fā)布時間:2024/4/11 编程问答 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 手把手教你玩转SOCKET模型:重叠I/O篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

“身為一個初學(xué)者,時常能體味到初學(xué)者入門的艱辛,所以總是想抽空作點什么來盡我所能的幫助那些需要幫助的人。我也希望大家能把自己的所學(xué)和他人一起分享,不要去鄙視別人索取時的貪婪,因為最應(yīng)該被鄙視的是不肯付出時的吝嗇。”

----- 題記 By PiggyXP(小豬)

前 言

其實我首先應(yīng)該道歉,因為7月份的時候曾信誓旦旦的說要寫一套關(guān)于SOCKET所有模型的入門文章以及配套代碼,不過沒想到后天竟然被美女所迷出去度假了,剛剛回來不久。。。。。。-_-b其實那些模型的配套代碼我已經(jīng)基本寫完了,只是沒寫配套文字,不過我想還是先從稍微難一點的模型寫起吧,因為其他模型的入門畢竟要簡單一些。

不過由于也是初學(xué)者,疏漏之處還望不吝指正。

本文凝聚著筆者心血,如要轉(zhuǎn)載,請指明原作者及出處,謝謝!^_^

OK, Let’s go ! Have fun!! q^_^p

(本文假設(shè)你已經(jīng)具備用SOCKET簡單模型編程的能力,如果對SOCKET一無所知請關(guān)注本系列其他文章)

目錄:

1. 重疊模型的優(yōu)點

2. 重疊模型的基本原理

3. 關(guān)于重疊模型的基礎(chǔ)知識

4. 重疊模型的實現(xiàn)步驟

5. 多客戶端情況的注意事項

一. 重疊模型的優(yōu)點

1. 可以運行在支持Winsock2的所有Windows平臺 ,而不像完成端口只是支持NT系統(tǒng)。

2. 比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重疊I/O(Overlapped I/O)模型使應(yīng)用程序能達到更佳的系統(tǒng)性能。

因為它和這4種模型不同的是,使用重疊模型的應(yīng)用程序通知緩沖區(qū)收發(fā)系統(tǒng)直接使用數(shù)據(jù),也就是說,如果應(yīng)用程序投遞了一個10KB大小的緩沖區(qū)來接收數(shù)據(jù),且數(shù)據(jù)已經(jīng)到達套接字,則該數(shù)據(jù)將直接被拷貝到投遞的緩沖區(qū)。

而這4種模型種,數(shù)據(jù)到達并拷貝到單套接字接收緩沖區(qū)中,此時應(yīng)用程序會被告知可以讀入的容量。當(dāng)應(yīng)用程序調(diào)用接收函數(shù)之后,數(shù)據(jù)才從單套接字緩沖區(qū)拷貝到應(yīng)用程序的緩沖區(qū),差別就體現(xiàn)出來了。

3. 從《windows網(wǎng)絡(luò)編程》中提供的試驗結(jié)果中可以看到,在使用了P4 1.7G Xero處理器(CPU很強啊)以及768MB的回應(yīng)服務(wù)器中,最大可以處理4萬多個SOCKET連接,在處理1萬2千個連接的時候CPU占用率才40% 左右 ―― 非常好的性能,已經(jīng)直逼完成端口了^_^

二. 重疊模型的基本原理

說了這么多的好處,你一定也躍躍欲試了吧,不過我們還是要先提一下重疊模型的基本原理。

概括一點說,重疊模型是讓應(yīng)用程序使用重疊數(shù)據(jù)結(jié)構(gòu)(WSAOVERLAPPED),一次投遞一個或多個Winsock I/O請求。針對這些提交的請求,在它們完成之后,應(yīng)用程序會收到通知,于是就可以通過自己另外的代碼來處理這些數(shù)據(jù)了。

需要注意的是,有兩個方法可以用來管理重疊IO請求的完成情況(就是說接到重疊操作完成的通知):

1. 事件對象通知(event object notification)

2. 完成例程(completion routines) ,注意,這里并不是完成端口

而本文只是講述如何來使用事件通知的的方法實現(xiàn)重疊IO模型,完成例程的方法準(zhǔn)備放到下一篇講 :) (內(nèi)容太多了,一篇寫不完啊) ,如沒有特殊說明,本文的重疊模型默認就是指的基于事件通知的重疊模型。

既然是基于事件通知,就要求將Windows事件對象與WSAOVERLAPPED結(jié)構(gòu)關(guān)聯(lián)在一起(WSAOVERLAPPED結(jié)構(gòu)中專門有對應(yīng)的參數(shù)),通俗一點講,就是。。。。對了,忘了說了,既然要使用重疊結(jié)構(gòu),我們常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替換掉了, 它們的用法我后面會講到,這里只需要注意一點,它們的參數(shù)中都有一個Overlapped參數(shù),我們可以假設(shè)是把我們的WSARecv這樣的操作操作“綁定”到這個重疊結(jié)構(gòu)上,提交一個請求,其他的事情就交給重疊結(jié)構(gòu)去操心,而其中重疊結(jié)構(gòu)又要與Windows的事件對象“綁定”在一起,這樣我們調(diào)用完WSARecv以后就可以“坐享其成”,等到重疊操作完成以后,自然會有與之對應(yīng)的事件來通知我們操作完成,然后我們就可以來根據(jù)重疊操作的結(jié)果取得我們想要德數(shù)據(jù)了。

也許說了半天你還是不大明白,那就繼續(xù)往后面看吧。。。。。。。-_-b,語言表達能力有限啊~~~

三. 關(guān)于重疊模型的基礎(chǔ)知識

下面來介紹并舉例說明一下編寫重疊模型的程序中將會使用到的幾個關(guān)鍵函數(shù)。

1. WSAOVERLAPPED結(jié)構(gòu)

這個結(jié)構(gòu)自然是重疊模型里的核心,它是這么定義的

typedef struct _WSAOVERLAPPED {?? DWORD Internal;?? DWORD InternalHigh;?? DWORD Offset;?? DWORD OffsetHigh;?? WSAEVENT hEvent;????? // 唯一需要關(guān)注的參數(shù),用來關(guān)聯(lián)WSAEvent對象 } WSAOVERLAPPED, *LPWSAOVERLAPPED; 我們需要把WSARecv等操作投遞到一個重疊結(jié)構(gòu)上,而我們又需要一個與重疊結(jié)構(gòu)“綁定”在一起的事件對象來通知我們操作的完成,看到了和hEvent參數(shù),不用我說你們也該知道如何來來把事件對象綁定到重疊結(jié)構(gòu)上吧?大致如下: WSAEVENT event;?????????????????? // 定義事件 WSAOVERLAPPED AcceptOverlapped ; // 定義重疊結(jié)構(gòu) event = WSACreateEvent();???????? // 建立一個事件對象句柄 ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重疊結(jié)構(gòu) AcceptOverlapped.hEvent = event;??? // Done !! ?

2. WSARecv系列函數(shù)

在重疊模型中,接收數(shù)據(jù)就要靠它了,它的參數(shù)也比recv要多,因為要用刀重疊結(jié)構(gòu)嘛,它是這樣定義的:

??????????? int WSARecv( ??????????????????????? SOCKET s,????????????????????? // 當(dāng)然是投遞這個操作的套接字 ??????????????????????? LPWSABUF lpBuffers,????????? // 接收緩沖區(qū),與Recv函數(shù)不同 // 這里需要一個由WSABUF結(jié)構(gòu)構(gòu)成的數(shù)組 ???????? DWORD dwBufferCount,??????? // 數(shù)組中WSABUF結(jié)構(gòu)的數(shù)量 ??????????? LPDWORD lpNumberOfBytesRecvd,? // 如果接收操作立即完成,這里會返回函數(shù)調(diào)用 // 所接收到的字節(jié)數(shù) ????????? LPDWORD lpFlags,???????????? // 說來話長了,我們這里設(shè)置為0 即可 ????????? LPWSAOVERLAPPED lpOverlapped, ?// “綁定”的重疊結(jié)構(gòu) ????????? LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ?????????????????????????????? // 完成例程中將會用到的參數(shù),我們這里設(shè)置為 NULL ??????????????? ); 返回值: WSA_IO_PENDING : 最常見的返回值,這是說明我們的WSARecv操作成功了,但是 ??????????????????? I/O操作還沒有完成,所以我們就需要綁定一個事件來通知我們操作何時完成 ? 舉個例子:(變量的定義順序和上面的說明的順序是對應(yīng)的,下同) SOCKET s; WSABUF DataBuf;?????????? // 定義WSABUF結(jié)構(gòu)的緩沖區(qū) // 初始化一下DataBuf #define DATA_BUFSIZE 5096 char buffer[DATA_BUFSIZE]; ZeroMemory(buffer, DATA_BUFSIZE); DataBuf.len = DATA_BUFSIZE; DataBuf.buf = buffer; DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0; // 建立需要的重疊結(jié)構(gòu) WSAOVERLAPPED AcceptOverlapped ;// 如果要處理多個操作,這里當(dāng)然需要一個 // WSAOVERLAPPED數(shù)組 WSAEVENT event;???? // 如果要多個事件,這里當(dāng)然也需要一個WSAEVENT數(shù)組 ?????????????????????????? // 需要注意的是可能一個SOCKET同時會有一個以上的重疊請求, //? 也就會對應(yīng)一個以上的WSAEVENT Event = WSACreateEvent(); ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); AcceptOverlapped.hEvent = event;???? // 關(guān)鍵的一步,把事件句柄“綁定”到重疊結(jié)構(gòu)上 // 作了這么多工作,終于可以使用WSARecv來把我們的請求投遞到重疊結(jié)構(gòu)上了,呼。。。。 WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes, &Flags, &AcceptOverlapped, NULL); 其他的函數(shù)我這里就不一一介紹了,因為我們畢竟還有MSDN這么個好幫手,而且在講后面的完成例程和完成端口的時候我還會講到一些 ^_^ ?

3. WSAWaitForMultipleEvents函數(shù)

熟悉WSAEventSelect模型的朋友對這個函數(shù)肯定不會陌生,不對,其實大家都不應(yīng)該陌生,這個函數(shù)與線程中常用的WaitForMultipleObjects函數(shù)有些地方還是比較像的,因為都是在等待某個事件的觸發(fā)嘛。

因為我們需要事件來通知我們重疊操作的完成,所以自然需要這個等待事件的函數(shù)與之配套。

??????????????????????? DWORD WSAWaitForMultipleEvents( ????????????????????????? ??????? DWORD cEvents,??????????????????????? // 等候事件的總數(shù)量 ????????????????????????????????? const WSAEVENT* lphEvents,?????????? // 事件數(shù)組的指針 ????????????????????????????????? BOOL fWaitAll,????????? // 這個要多說兩句: ????????????????????????????????????????????????????????????????? // 如果設(shè)置為 TRUE,則事件數(shù)組中所有事件被傳信的時候函數(shù)才會返回 ????????????????????????????????????????????????????????????????? // FALSE則任何一個事件被傳信函數(shù)都要返回 ????????????????????????????????????????????????????????????????? // 我們這里肯定是要設(shè)置為FALSE的 ????????????????????????????????? DWORD dwTimeout,??? // 超時時間,如果超時,函數(shù)會返回 WSA_WAIT_TIMEOUT ?????????????????????????????? // 如果設(shè)置為0,函數(shù)會立即返回 ??????????????????????????? // 如果設(shè)置為 WSA_INFINITE只有在某一個事件被傳信后才會返回 ??????????????????????????? // 在這里不建議設(shè)置為WSA_INFINITE,因為。。。后面再講吧..-_-b ????????????????????????????????? BOOL fAlertable?????? // 在完成例程中會用到這個參數(shù),這里我們先設(shè)置為FALSE ??????????????????????????????? ); 返回值: ??? WSA_WAIT_TIMEOUT :最常見的返回值,我們需要做的就是繼續(xù)Wait ??? WSA_WAIT_FAILED : 出現(xiàn)了錯誤,請檢查cEvents和lphEvents兩個參數(shù)是否有效 如果事件數(shù)組中有某一個事件被傳信了,函數(shù)會返回這個事件的索引值,但是這個索引值需要減去預(yù)定義值 WSA_WAIT_EVENT_0才是這個事件在事件數(shù)組中的位置。 具體的例子就先不在這里舉了,后面還會講到 注意:WSAWaitForMultipleEvents函數(shù)只能支持由WSA_MAXIMUM_WAIT_EVENTS對象定義的一個最大值,是 64,就是說WSAWaitForMultipleEvents只能等待64個事件,如果想同時等待多于64個事件,就要 創(chuàng)建額外的工作者線程,就不得不去管理一個線程池,這一點就不如下一篇要講到的完成例程模型了。

4. WSAGetOverlappedResult函數(shù)

既然我們可以通過WSAWaitForMultipleEvents函數(shù)來得到重疊操作完成的通知,那么我們自然也需要一個函數(shù)來查詢一下重疊操作的結(jié)果,定義如下

??????????? BOOL WSAGetOverlappedResult( ????????????????????????? SOCKET s,?????????????????? // SOCKET,不用說了 ????????????????????????? LPWSAOVERLAPPED lpOverlapped,? // 這里是我們想要查詢結(jié)果的那個重疊結(jié)構(gòu)的指針 ????????????????????????? LPDWORD lpcbTransfer,???? // 本次重疊操作的實際接收(或發(fā)送)的字節(jié)數(shù) ????????????????????????? BOOL fWait,??????????????? // 設(shè)置為TRUE,除非重疊操作完成,否則函數(shù)不會返回 ????????????????????????????????????????????????????????????? // 設(shè)置FALSE,而且操作仍處于掛起狀態(tài),那么函數(shù)就會返回FALSE ????????????????????????????????????????????????????????????? // 錯誤為WSA_IO_INCOMPLETE ????????????????????????????????????????????????????????????? // 不過因為我們是等待事件傳信來通知我們操作完成,所以我們這里設(shè) ??????????????? // 置成什么都沒有作用…..-_-b? 別仍雞蛋啊,我也想說得清楚一些… ????????????????????????? LPDWORD lpdwFlags?????? // 指向DWORD的指針,負責(zé)接收結(jié)果標(biāo)志 ??????????????????????? ); 這個函數(shù)沒什么難的,這里我們也不需要去關(guān)注它的返回值,直接把參數(shù)填好調(diào)用就可以了,這里就先不舉例了 唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三個參數(shù)返回是 0 ,則說明通信對方已經(jīng)關(guān)閉連接,我們這邊的SOCKET, Event之類的也就可以關(guān)閉了。

四、實現(xiàn)重疊模型的步驟

作了這么多的準(zhǔn)備工作,費了這么多的筆墨,我們終于可以開始著手編碼了。其實慢慢的你就會明白,要想透析重疊結(jié)構(gòu)的內(nèi)部原理也許是要費點功夫,但是只是學(xué)會如何來使用它,卻是真的不難,唯一需要理清思路的地方就是和大量的客戶端交互的情況下,我們得到事件通知以后,如何得知是哪一個重疊操作完成了,繼而知道究竟該對哪一個套接字進行處理,應(yīng)該去哪個緩沖區(qū)中的取得數(shù)據(jù),everything will be OK^_^。

下面我們配合代碼,來一步步的講解如何親手完成一個重疊模型。

第一步定義變量…………

#define DATA_BUFSIZE???? 4096????????? // 接收緩沖區(qū)大小 SOCKET???????? ListenSocket,???????????? // 監(jiān)聽套接字 AcceptSocket;???????????? // 與客戶端通信的套接字 WSAOVERLAPPED? AcceptOverlapped;???? // 重疊結(jié)構(gòu)一個 WSAEVENT? EventArray[WSA_MAXIMUM_WAIT_EVENTS];? // 用來通知重疊操作完成的事件句柄數(shù)組 WSABUF???? DataBuf[DATA_BUFSIZE] ;????? DWORD???? dwEventTotal = 0,??????????? // 程序中事件的總數(shù) ???????????? dwRecvBytes = 0,??????????? // 接收到的字符長度 ?????????????????? Flags = 0;??????????????????? // WSARecv的參數(shù) ?

【第二步】創(chuàng)建一個套接字,開始在指定的端口上監(jiān)聽連接請求

和其他的SOCKET初始化全無二致,直接照搬即可,在此也不多費唇舌了,需要注意的是為了一目了然,我去掉了錯誤處理,平常可不要這樣啊,盡管這里出錯的幾率比較小。

WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); ? ListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);? //創(chuàng)建TCP套接字 ? SOCKADDR_IN ServerAddr;?????????????????????????? //分配端口及協(xié)議族并綁定 ServerAddr.sin_family=AF_INET;??????????????????????????????? ServerAddr.sin_addr.S_un.S_addr? =htonl(INADDR_ANY);????????? ServerAddr.sin_port=htons(11111); ? bind(ListenSocket,(LPSOCKADDR)&ServerAddr, sizeof(ServerAddr)); // 綁定套接字 ? listen(ListenSocket, 5);?????????????????????????????????? //開始監(jiān)聽

【第三步】接受一個入站的連接請求

一個accept就完了,都是一樣一樣一樣一樣的啊~~~~~~~~~~

至于AcceptEx的使用,在完成端口中我會講到,這里就先不一次灌輸這么多了,不消化啊^_^

AcceptSocket = accept (ListenSocket, NULL,NULL) ;

當(dāng)然,這里是我偷懶,如果想要獲得連入客戶端的信息(記得論壇上也常有人問到),accept的后兩個參數(shù)就不要用NULL,而是這樣

SOCKADDR_IN ClientAddr;?????????????????? // 定義一個客戶端得地址結(jié)構(gòu)作為參數(shù) int addr_length=sizeof(ClientAddr); AcceptSocket = accept(ListenSocket,(SOCKADDR*)&ClientAddr, &addr_length); // 于是乎,我們就可以輕松得知連入客戶端的信息了 LPCTSTR lpIP =? inet_ntoa(ClientAddr.sin_addr);????? // IP UINT nPort = ClientAddr.sin_port;????????????????????? // Port

【第四步】建立并初始化重疊結(jié)構(gòu)

為連入的這個套接字新建立一個WSAOVERLAPPED重疊結(jié)構(gòu),并且象前面講到的那樣,為這個重疊結(jié)構(gòu)從事件句柄數(shù)組里挑出一個空閑的對象句柄“綁定”上去。

// 創(chuàng)建一個事件 // dwEventTotal可以暫時先作為Event數(shù)組的索引 EventArray[dwEventTotal] = WSACreateEvent();????? ? ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));????? // 置零 AcceptOverlapped.hEvent = EventArray[dwEventTotal];??????????? // 關(guān)聯(lián)事件 ? char buffer[DATA_BUFSIZE]; ZeroMemory(buffer, DATA_BUFSIZE); DataBuf.len = DATA_BUFSIZE; DataBuf.buf = buffer;????????????????????????? // 初始化一個WSABUF結(jié)構(gòu) dwEventTotal ++;????????????????????????????? // 總數(shù)加一

【第五步】以WSAOVERLAPPED結(jié)構(gòu)為參數(shù),在套接字上投遞WSARecv請求

各個變量都已經(jīng)初始化OK以后,我們就可以開始Socket操作了,然后讓W(xué)SAOVERLAPPED結(jié)構(gòu)來替我們管理I/O 請求,我們只用等待事件的觸發(fā)就OK了。

if(WSARecv(AcceptSocket ,&DataBuf,1,&dwRecvBytes,&Flags, ??????????????????????????????????????? & AcceptOverlapped, NULL) == SOCKET_ERROR) { ?? // 返回WSA_IO_PENDING是正常情況,表示IO操作正在進行,不能立即完成 ?? // 如果不是WSA_IO_PENDING錯誤,就大事不好了~~~~~~!!! ????? if(WSAGetLastError() != WSA_IO_PENDING)??? ????? { ???????????????? // 那就只能關(guān)閉大吉了 ???????????????????????? closesocket(AcceptSocket); ???????????????????????? WSACloseEvent(EventArray[dwEventTotal]); ???????? } }

【第六步】?用WSAWaitForMultipleEvents函數(shù)等待重疊操作返回的結(jié)果

我們前面已經(jīng)給WSARecv關(guān)聯(lián)的重疊結(jié)構(gòu)賦了一個事件對象句柄,所以我們這里要等待事件對象的觸發(fā)與之配合,而且需要根據(jù)WSAWaitForMultipleEvents函數(shù)的返回值來確定究竟事件數(shù)組中的哪一個事件被觸發(fā)了,這個函數(shù)的用法及返回值請參考前面的基礎(chǔ)知識部分。

DWORD dwIndex; // 等候重疊I/O調(diào)用結(jié)束 // 因為我們把事件和Overlapped綁定在一起,重疊操作完成后我們會接到事件通知 dwIndex = WSAWaitForMultipleEvents(dwEventTotal, EventArray ,FALSE ,WSA_INFINITE,FALSE); // 注意這里返回的Index并非是事件在數(shù)組里的Index,而是需要減去WSA_WAIT_EVENT_0 dwIndex = dwIndex – WSA_WAIT_EVENT_0;

【第七步】使用WSAResetEvent函數(shù)重設(shè)當(dāng)前這個用完的事件對象

事件已經(jīng)被觸發(fā)了之后,它對于我們來說已經(jīng)沒有利用價值了,所以要將它重置一下留待下一次使用,很簡單,就一步,連返回值都不用考慮

WSAResetEvent(EventArray[dwIndex]);

【第八步】使用WSAGetOverlappedResult函數(shù)取得重疊調(diào)用的返回狀態(tài)

這是我們最關(guān)心的事情,費了那么大勁投遞的這個重疊操作究竟是個什么結(jié)果呢?其實對于本模型來說,唯一需要檢查一下的就是對方的Socket連接是否已經(jīng)關(guān)閉了

DWORD dwBytesTransferred; WSAGetOverlappedResult( AcceptSocket, AcceptOverlapped , &dwBytesTransferred, FALSE, &Flags); // 先檢查通信對方是否已經(jīng)關(guān)閉連接 // 如果==0則表示連接已經(jīng),則關(guān)閉套接字 if(dwBytesTransferred == 0) { ???????? closesocket(AcceptSocket); ????? WSACloseEvent(EventArray[dwIndex]);??? // 關(guān)閉事件 ???????? return; }

【第九步】“享受”接收到的數(shù)據(jù)

如果程序執(zhí)行到了這里,那么就說明一切正常,WSABUF結(jié)構(gòu)里面就存有我們WSARecv來的數(shù)據(jù)了,終于到了盡情享用成果的時候了!喝杯茶,休息一下吧~~~^_^

DataBuf.buf就是一個char*字符串指針,聽?wèi){你的處理吧,我就不多說了

【第十步】同第五步一樣,在套接字上繼續(xù)投遞WSARecv請求,重復(fù)步驟?6 ~ 9

這樣一路作下來,我們終于可以從客戶端接收到數(shù)據(jù)了,但是回想起來,呀~~~~~,這樣豈不是只能收到一次數(shù)據(jù),然后程序不就Over了?…….-_-b 所以我們接下來不得不重復(fù)一遍第四步和第五步的工作,再次在這個套接字上投遞另一個WSARecv請求,并且使整個過程循環(huán)起來,are u clear??

大家可以參考我的代碼,在這里就先不寫了,因為各位都一定比我smart,領(lǐng)悟了關(guān)鍵所在以后,稍作思考就可以靈活變通了。

五、多客戶端情況的注意事項

完成了上面的循環(huán)以后,重疊模型就已經(jīng)基本上搭建好了80%了,為什么不是100%呢?因為仔細一回想起來,呀~~~~~~~,這樣豈不是只能連接一個客戶端??是的,如果只處理一個客戶端,那重疊模型就半點優(yōu)勢也沒有了,我們正是要使用重疊模型來處理多個客戶端。

所以我們不得不再對結(jié)構(gòu)作一些改動。

1.?首先,肯定是需要一個SOCKET數(shù)組 ,分別用來和每一個SOCKET通信

其次,因為重疊模型中每一個SOCKET操作都是要“綁定”一個重疊結(jié)構(gòu)的,所以需要為每一個SOCKET操作搭配一個WSAOVERLAPPED結(jié)構(gòu),但是這樣說并不嚴格,因為如果每一個SOCKET同時只有一個操作,比如WSARecv,那么一個SOCKET就可以對應(yīng)一個WSAOVERLAPPED結(jié)構(gòu),但是如果一個SOCKET上會有WSARecv 和WSASend兩個操作,那么一個SOCKET肯定就要對應(yīng)兩個WSAOVERLAPPED結(jié)構(gòu),所以有多少個SOCKET操作就會有多少個WSAOVERLAPPED結(jié)構(gòu)。

然后,同樣是為每一個WSAOVERLAPPED結(jié)構(gòu)都要搭配一個WSAEVENT事件,所以說有多少個SOCKET操作就應(yīng)該有多少個WSAOVERLAPPED結(jié)構(gòu),有多少個WSAOVERLAPPED結(jié)構(gòu)就應(yīng)該有多少個WSAEVENT事件,最好把SOCKET – WSAOVERLAPPED – WSAEVENT三者的關(guān)聯(lián)起來,到了關(guān)鍵時刻才會臨危不亂:)

2.?不得不分作兩個線程:

一個用來循環(huán)監(jiān)聽端口,接收請求的連接,然后給在這個套接字上配合一個WSAOVERLAPPED結(jié)構(gòu)投遞第一個WSARecv請求,然后進入第二個線程中等待操作完成。

第二個線程用來不停的對WSAEVENT數(shù)組WSAWaitForMultipleEvents,等待任何一個重疊操作的完成,然后根據(jù)返回的索引值進行處理,處理完畢以后再繼續(xù)投遞另一個WSARecv請求。

這里需要注意一點的是,前面我是把WSAWaitForMultipleEvents函數(shù)的參數(shù)設(shè)置為WSA_

INFINITE的,但是在多客戶端的時候這樣就不OK了,需要設(shè)定一個超時時間,如果等待超時了再重新WSAWaitForMultipleEvents,因為WSAWaitForMultipleEvents函數(shù)在沒有觸發(fā)的時候是阻塞在那里的,我們可以設(shè)想一下,這時如果監(jiān)聽線程忠接入了新的連接,自然也會為這個連接增加一個Event,但是WSAWaitForMultipleEvents還是阻塞在那里就不會處理這個新連接的Event了。也不知道說明白了沒有。。。。。。-_-b 可能在這里你也體會不到,真正編碼的時候就會明白了。

其他還有不明白的地方可以參考我的代碼,代碼里也有比較詳盡的注釋, Enjoy~~~

不過可惜是為了照顧大多數(shù)人,使用的是MFC的代碼,顯得代碼有些雜亂。

六.?已知問題

這個已知問題是說我的代碼中的已知問題,可不是重疊結(jié)構(gòu)的已知問題:)

這個示例代碼已經(jīng)寫好了很久了,這兩天做最后測試的時候才發(fā)現(xiàn)竟然有兩個Bug,而且還不是每次都會出現(xiàn),5555,我最近是實在沒有精力去改了,如果有心的朋友能修改掉這兩個Bug,那真是造福大家了,這篇文章都險些流產(chǎn),我更沒有經(jīng)歷去修改都快要淡忘了的代碼的Bug了,我寫在這里提醒一下大家了,反正這個代碼也僅僅是拋磚引玉而已,而且我覺得比起代碼來還是文字比較珍貴^_^,因為重疊模型的代碼網(wǎng)上也還是有不少的。兩個Bug是這樣的:

1. 多個客戶端在連續(xù)退出的時候,有時會出現(xiàn)異常;

2. 有時多個客戶端的接收緩沖區(qū)竟然會重疊到一起,就是說A客戶端發(fā)送的數(shù)據(jù)后面會根有B客戶端上次發(fā)來的數(shù)據(jù)。。。。。-_-b

改進算法:其實代碼中的算法還有很多可以改進的地方,limin朋友就向我提及過幾個非常好的改進算法,比如如何在socket數(shù)組中尋找空閑的socket用來通信,但是我并沒有加到這份代碼里面來,因為本來重疊模型的代碼就比較雜,再加上這些東西恐怕反而會給初學(xué)者帶來困難。但是非常歡迎各位和我討論重疊模型的改進算法以及我代碼中存在問題!^_^

就說這么多吧,但愿你能通過這篇文章熟練的玩轉(zhuǎn)重疊IO模型,就沒有枉費我這番功夫了。^_^

總結(jié)

以上是生活随笔為你收集整理的手把手教你玩转SOCKET模型:重叠I/O篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。