详细解析WSAAsyncSelect模型
介紹
WinSock是Windows提供的包含了一系列網(wǎng)絡(luò)編程接口的套接字程序庫。在這篇文章中,我們將介紹如何把它的非阻塞模式引入到應(yīng)用程序中。
阻塞模式WinSock.下述偽代碼給出了阻塞模式下WinSock的使用方式。
//服務(wù)器
WSAStartup();
SOCKET server = socket();
bind(server);
listen(server);
SOCKET client = accept(server);
send(client);
recv(client);
closesocket(client);
closesocket(server);?
WSACleanup();
//客戶端
WSAStartup();
SOCKET client=socket();?
bind(client);
ServerAddress server;
connect(client, server);
recv(client);
send(client);
closesocket(client);
WSACleanup();
代碼中,服務(wù)器端的accept(),客戶端的connect(),以及服務(wù)器和客戶端中共同的recv()、send()函數(shù)均會產(chǎn)生阻塞。
服務(wù)器在調(diào)用accept()后不會返回,直到接收到客戶端的連接請求;
客戶端在調(diào)用connect()后不會返回,直到對服務(wù)器連接成功或者失敗;
服務(wù)器和客戶端在調(diào)用recv()后不會返回,直到接收到并讀取完一條消息;
服務(wù)器和客戶端在調(diào)用send()后不會返回,直到發(fā)送完待發(fā)送的消息。
如果這兩段代碼被放在Windows程序的主線程中,你會發(fā)現(xiàn)消息循環(huán)被阻塞,程序不再響應(yīng)用戶輸入及重繪請求。為了解決這個問題,
你可能會想到開辟另外一個線程來運行這些代碼。這是可行的,但是考慮到每個SOCKET都不應(yīng)該被其他SOCKET的操作所阻塞,是不是
需要為每個SOCKET開辟一個線程?再考慮到同一SOCKET的一個讀寫操作也不應(yīng)該被另外一個讀寫操作所阻塞,是不是應(yīng)該再為每個
SOCKET的讀和寫分別開辟一個線程?一般來說,這種自實現(xiàn)的多線程解決方案帶來的諸多線程管理方面的問題,是你絕對不會想要遇
到的。
?
非阻塞模式WinSock
所幸的是,WinSock同時提供了非阻塞模式,并提出了幾種I/O模型。最常見的I/O模型有select模型、WSAAsyncSelect模型及
WSAEventSelect模型,下面選擇其中的WSAAsyncSelect模型進(jìn)行介紹。使用WSAAsyncSelect模型將非阻塞模式引入到應(yīng)用程序中的過
程看起來很簡單,事實上你只需要多添加一個函數(shù)就夠了。
int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
該函數(shù)會自動將套接字設(shè)置為非阻塞模式,并且把發(fā)生在該套接字上且是你所感興趣的事件,以Windows消息的形式發(fā)送到指定的窗口,
你需要做的就是在傳統(tǒng)的消息處理函數(shù)中處理這些事件。參數(shù)hWnd表示指定接受消息的窗口句柄;參數(shù)wMsg表示消息碼值(這意味著你
需要自定義一個Windows消息碼);參數(shù)IEvent表示你希望接受的網(wǎng)絡(luò)事件的集合,它可以是如下值的任意組合:FD_READ, FD_WRITE,?
FD_OOB, FD_ACCEPT, FD_CONNECT, FD_CLOSE 之后,就可以在我們熟知的Windows消息處理函數(shù)中處理這些事件。如果在某一套接字s上
發(fā)生了一個已命名的網(wǎng)絡(luò)事件,應(yīng)用程序窗口hWnd會接收到消息wMsg。參數(shù)wParam即為該事件相關(guān)的套接字s;參數(shù)lParam的低字段指
明了發(fā)生的網(wǎng)絡(luò)事件,lParam的高字段則含有一個錯誤碼,事件和錯誤碼可以通過下面的宏從lParam中取出:
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
下面繼續(xù)使用偽代碼來幫助闡述如何將上一節(jié)的阻塞模式WinSock應(yīng)用升級到非阻塞模式。
首先自定義一個Windows消息碼,用于標(biāo)識我們的網(wǎng)絡(luò)消息。
#define WM_CUSTOM_NETWORK_MSG (WM_USER + 100)?
//服務(wù)器端,在監(jiān)聽之前,將監(jiān)聽套接字置為非阻塞模式,并且標(biāo)明其感興趣的事件為FD_ACCEPT。
WSAAsyncSelect(server, wnd, WM_CUSTOM_NETWORK_MSG, FD_ACCEPT);?
listen(server);?
//客戶端,在連接之前,將套接字置為非阻塞模式,并標(biāo)明其感興趣的事件為FD_CONNECT。
WSAAsyncSelect(client, wnd, WM_CUSTOM_NETWORK_MSG, FD_CONNECT);
ServerAddress?server;
connect(client,?server);
//接著,在Windows消息處理函數(shù)中,我們將處理監(jiān)聽事件、連接事件、及讀寫事件,方便起見,這里將服務(wù)器和客戶端的處理代碼放在
了一起。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ? ?
{ ? ?
? ? switch (message) ? ?
? ? {
? ? case WM_CUSTOM_NETWORK_MSG: // 自定義的網(wǎng)絡(luò)消息碼 ? ?
? ? ? ? { ? ?
? ? ? ? ? ? SOCKET socket = (SOCKET)wParam; // 發(fā)生網(wǎng)絡(luò)事件的套接字 ? ?
? ? ? ? ? ? long event = WSAGETSELECTEVENT(lParam); // 事件 ? ?
? ? ? ? ? ? int error = WSAGETSELECTERROR(lParam); // 錯誤碼 ? ?
? ??
? ? ? ? ? ? switch (event) ? ?
? ? ? ? ? ? { ? ?
? ? ? ? ? ? case FD_ACCEPT: // 服務(wù)器收到新客戶端的連接請求 ? ?
? ? ? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? ? ? // 接收到客戶端連接,分配一個客戶端套接字 ? ?
? ? ? ? ? ? ? ? ? ? SOCKET client = accept(socket); ? ??
? ? ? ? ? ? ? ? ? ? // 將新分配的客戶端套接字置為非阻塞模式,并標(biāo)明其感興趣的事件為讀、寫及關(guān)閉 ? ?
? ? ? ? ? ? ? ? ? ? WSAAsyncSelect(client, hWnd, message, FD_READ | FD_WRITE | FD_CLOSE); ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? case FD_CONNECT: // 客戶端連接到服務(wù)器的操作返回結(jié)果 ? ?
? ? ? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? ? ? // 成功連接到服務(wù)器,將客戶端套接字置為非阻塞模式,并標(biāo)明其感興趣的事件為讀、寫及關(guān)閉 ? ?
? ? ? ? ? ? ? ? ? ? WSAAsyncSelect(socket, hWnd, message, FD_READ | FD_WRITE | FD_CLOSE); ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? case FD_READ: // 收到網(wǎng)絡(luò)包,需要讀取 ? ?
? ? ? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? ? ? // 使用套接字讀取網(wǎng)絡(luò)包 ? ?
? ? ? ? ? ? ? ? ? ? recv(socket); ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? case FD_WRITE: ? ?
? ? ? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? ? ? // FD_WRITE的處理后面會具體討論 ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? case FD_CLOSE: // 套接字的連接方(而非本地socket)關(guān)閉消息 ? ?
? ? ? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? default: ? ?
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? } ? ?
? ? ? ? } ? ?
? ? ? ? break; ? ?
? ? … ? ?
? ? } ? ?
? ? … ? ?
} ? ?
以上就是非阻塞模式WinSock的應(yīng)用框架,WSAAsyncSelect模型將套接字和Windows消息機制很好地粘合在一起,為用戶異步SOCKET應(yīng)用提供
了一種較優(yōu)雅的解決方案。
擴展討論
WinSock在系統(tǒng)底層為套接字收發(fā)網(wǎng)絡(luò)數(shù)據(jù)各提供一個緩沖區(qū),接收到的網(wǎng)絡(luò)數(shù)據(jù)會緩存在這里等待應(yīng)用程序讀取,待發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)也會先
寫進(jìn)這里之后通過網(wǎng)絡(luò)發(fā)送。相關(guān)的,針對FD_READ和FD_WRITE事件的讀寫處理,因涉及的內(nèi)容稍微復(fù)雜而容易使人困惑,這里需要特別進(jìn)行
討論。在FD_READ事件中,使用recv()函數(shù)讀取網(wǎng)絡(luò)包數(shù)據(jù)時,由于事先并不知道完整網(wǎng)絡(luò)包的大小,所以需要多次讀取直到讀完整個緩沖區(qū)
。這就需要類似如下代碼的調(diào)用:
void* buf = 0; ? ?
int size = 0; ? ?
while (true) ? ?
{ ? ?
? ? char tmp[128]; ? ?
? ? int bytes = recv(socket, tmp, 128, 0); ? ?
? ? if (bytes <= 0) ? ?
? ? ? ? break; ? ?
? ? else ? ?
? ? { ? ?
? ? ? ? int new_size = size + bytes; ? ?
? ? ? ? buf = realloc(buf, new_size); ? ?
? ? ? ? memcpy((void*)(((char*)buf) + size), tmp, bytes); ? ?
? ? ? ? size = new_size; ? ?
? ? } ? ?
} ? ?
//此時數(shù)據(jù)已經(jīng)從緩沖區(qū)全部拷貝到buf中,你可以在這里對buf做一些操作 ? ? ?
free(buf); ? ?
這一切看起來都沒有什么問題,但是如果程序運行起來,你會收到比預(yù)期多出許多的FD_READ事件。如MSDN所述,正常的情況下,應(yīng)用程序應(yīng)
當(dāng)為每一個FD_READ消息僅調(diào)用一次recv()函數(shù)。如果一個應(yīng)用程序需要在一個FD_READ事件處理中調(diào)用多次recv(),那么它將會收到多個
FD_READ消息,因為每次未讀完緩沖區(qū)的recv()調(diào)用,都會重新觸發(fā)一個FD_READ消息。針對這種情況,我們需要在讀取網(wǎng)絡(luò)包前關(guān)閉掉FD_READ
消息通知,讀取完這后再進(jìn)行恢復(fù),關(guān)閉FD_READ消息的方法很簡單,只需要調(diào)用WSAAsyncSelect時參數(shù)lEvent中FD_READ字段不予設(shè)置即可。
//關(guān)閉FD_READ事件通知 ? ?
WSAAsyncSelect(socket, hWnd, message, FD_WRITE | FD_CLOSE); ? ?
// 讀取網(wǎng)絡(luò)包 ? ?
… ? ?
// 再次打開FD_READ事件通知 ? ?
WSAAsyncSelect(socket, hWnd, message, FD_WRITE | FD_CLOSE | FD_READ); ? ?
第二個需要討論的是FD_WRITE事件。這個事件指明緩沖區(qū)已經(jīng)準(zhǔn)備就緒,有了多出的空位可以讓應(yīng)用程序?qū)懭霐?shù)據(jù)以供發(fā)送。該事件僅在兩種
情況下被觸發(fā):
1. 套接字剛建立連接時,表明準(zhǔn)備就緒可以立即發(fā)送數(shù)據(jù)。
2. 一次失敗的send()調(diào)用后緩沖區(qū)再次可用時。如果系統(tǒng)緩沖區(qū)已經(jīng)被填滿,那么此時調(diào)用send()發(fā)送數(shù)據(jù),將返回SOCKET_ERROR,使用
WSAGetLastError()會得到錯誤碼WSAEWOULDBLOCK表明被阻塞。這種情況下當(dāng)緩沖區(qū)重新整理出可用空間后,會向應(yīng)用程序發(fā)送FD_WRITE消息,
示意其可以繼續(xù)發(fā)送數(shù)據(jù)了。
所以說收到FD_WRITE消息并不單純地等同于這是使用send()的唯一時機。一般來說,如果需要發(fā)送消息,直接調(diào)用send()發(fā)送即可。如果該次
調(diào)用返回值為SOCKET_ERROR且WSAGetLastError()得到錯誤碼WSAEWOULDBLOCK,這意味著緩沖區(qū)已滿暫時無法發(fā)送,此刻我們需要將待發(fā)數(shù)據(jù)
保存起來,等到系統(tǒng)發(fā)出FD_WRITE消息后嘗試重新發(fā)送。也就是說,你需要針對FD_WRITE構(gòu)建一套數(shù)據(jù)重發(fā)的機制,文末的工程源碼里包含有
這套機制以供大家參考,這里不再贅述。
結(jié)語
至此,如何在非阻塞模式下使用WinSock進(jìn)行編程介紹完畢,這個框架可以滿足大多數(shù)網(wǎng)絡(luò)游戲客戶端及部分服務(wù)器的通信需求。更多應(yīng)用層面
上的問題(如TCP粘包等)這里沒有討論,或許會在以后的文章中給出。
WSAAsyncSelect模型(同步I/O模型)
這里為什么說他是同步的,就是因為實際的數(shù)據(jù)的Copy是同步進(jìn)行///的,而不是異步的,只是相應(yīng)的通知機制(通知數(shù)據(jù)已經(jīng)準(zhǔn)備好了),是異步的
這個模型允許應(yīng)用程序以Windows消息的形式可在一個套接字上,接收網(wǎng)絡(luò)事件通知
具體的做法是在建好一個套接字后,調(diào)用WSAAsyncSelect函數(shù)。
在我看來,WSAAsyncSelect是最簡單的一種Winsock I/O模型(之所以說它簡單是因為一個主線程就搞定了)。
這里,我們需要做的僅僅是:
1.在WM_CREATE消息處理函數(shù)中,初始化Windows Socket library,創(chuàng)建監(jiān)聽套接字,綁定,監(jiān)聽,并且調(diào)用WSAAsyncSelect函數(shù)表示我們關(guān)心在監(jiān)聽套接字上發(fā)生的FD_ACCEPT事件;
2.自定義一個消息WM_SOCKET,一旦在我們所關(guān)心的套接字(監(jiān)聽套接字和客戶端套接字)上發(fā)生了某個事件,系統(tǒng)就會調(diào)用WndProc并且message參數(shù)被設(shè)置為WM_SOCKET;
3.在WM_SOCKET的消息處理函數(shù)中,分別對FD_ACCEPT、FD_READ和FD_CLOSE事件進(jìn)行處理;
4.在窗口銷毀消息(WM_DESTROY)的處理函數(shù)中,我們關(guān)閉監(jiān)聽套接字,清除Windows Socket library
WSAAsyncSelect模型是Windows?socket的一個異步IO模型。利用該模型可以接收以Windows消息為基礎(chǔ)的網(wǎng)絡(luò)事件。Windows?sockets應(yīng)用程序在創(chuàng)建套接字后,調(diào)用WSAAsyncSelect函數(shù)注冊感興趣的網(wǎng)絡(luò)事件,當(dāng)該事件發(fā)生時Windows窗口收到消息,應(yīng)用程序就可以對接收到的網(wǎng)絡(luò)時間進(jìn)行處理。
?
WSAAsyncSelect是select模型的異步版本。在應(yīng)用程序使用select函數(shù)時會發(fā)生阻塞現(xiàn)象。可以通過select的timeout參數(shù)設(shè)置阻塞的時間。在設(shè)置的時間內(nèi),select函數(shù)等待,直到一個或多個套接字滿足可讀或可寫的條件。
而WSAAsyncSelect是非阻塞的。Windows?sockets程序在調(diào)用recv或send之前,調(diào)用WSAAsyncSelect注冊網(wǎng)絡(luò)事件。WSAAsyncSelect函數(shù)立即返回。當(dāng)系統(tǒng)中數(shù)據(jù)準(zhǔn)備好時,會向應(yīng)用程序發(fā)送消息。此此消息的處理函數(shù)中可以調(diào)用recv或send進(jìn)行接收或發(fā)送數(shù)據(jù)。
?
WSAAsyncSelect模型與select模型的相同點是它們都可以對多個套接字進(jìn)行管理。但它們也有不小的區(qū)別。首先WSAAsyncSelect模型是異步的,且通知方式不同。更重要的一點是:WSAAsyncSelect模型應(yīng)用在基于消息的Windows環(huán)境下,使用該模型時必須創(chuàng)建窗口,而select模型可以廣泛應(yīng)用在Unix系統(tǒng),使用該模型不需要創(chuàng)建窗口。最后一點區(qū)別:應(yīng)用程序在調(diào)用WSAAsyncSelect函數(shù)后,套接字就被設(shè)置為非阻塞狀態(tài)。而使用select函數(shù)不改變套接字的工作方式。
?
WSAAsyncSelect函數(shù)。
該函數(shù)告訴系統(tǒng)當(dāng)網(wǎng)絡(luò)事件發(fā)生時為套接字發(fā)送消息。聲明如下:?
[html]?view plaincopy?????s為需要通知的套接字。
?????hWnd為當(dāng)網(wǎng)絡(luò)事件發(fā)生時接收消息的窗口句柄。
?????wMsg為當(dāng)網(wǎng)絡(luò)事件發(fā)生時窗口收到的消息。在此消息的響應(yīng)函數(shù)內(nèi)對網(wǎng)絡(luò)事件進(jìn)行處理。
?????lEvent為應(yīng)用程序感興趣的網(wǎng)絡(luò)事件集合。
?????應(yīng)用程序調(diào)用該函數(shù)后自動將套接字設(shè)置為非阻塞模式。通常用戶自定義消息應(yīng)該在WM_USER的基礎(chǔ)之上定義。如WM_USER+1,以避免與Windows預(yù)定義的消息發(fā)生混淆。
?????網(wǎng)絡(luò)事件可以有以下幾種:
?
?????FD_READ:套接字可讀通知。
?????FD_WRITE:可寫通知。
?????FD_ACCEPT:服務(wù)器接收連接的通知。
?????FD_CONNECT:有客戶連接通知。
?????FD_OOB:外帶數(shù)據(jù)到達(dá)通知。
?????FD_CLOSE:套接字關(guān)閉通知。
?????FD_QOS:服務(wù)質(zhì)量發(fā)生變化通知。
?????FD_GROUP_QOS:組服務(wù)質(zhì)量發(fā)生變化通知。
?????FD_ROUTING_INTERFACE_CHANGE:與路由器接口發(fā)生變化的通知。
?????FD_ADDRESS_LIST_CHANGE:本地地址列表發(fā)生變化的通知。
?
?????開發(fā)人員應(yīng)向應(yīng)用程序注冊感興趣的網(wǎng)絡(luò)事件。可以將它們按位或并傳給lEvent函數(shù)。如:
[cpp]?view plaincopy?????上述代碼表示:當(dāng)套接字連接到來、有數(shù)據(jù)可讀或這套接字關(guān)閉的網(wǎng)絡(luò)事件發(fā)生時,WM_SOCKET消息就會發(fā)送給hWnd為句柄的窗口。
?????消息處理函數(shù)。
?????消息處理函數(shù)是對網(wǎng)絡(luò)事件發(fā)生時窗口消息的處理。它的聲明如下:
[cpp]?view plaincop?????hWnd為窗口句柄。
?????uMsg為當(dāng)網(wǎng)絡(luò)事件發(fā)生時的消息。
?????wParam為消息參數(shù)。該參數(shù)表明發(fā)生網(wǎng)絡(luò)事件的套接字。
?????lParam也為消息參數(shù)。低字節(jié)表明已發(fā)生的網(wǎng)絡(luò)事件。高字節(jié)包含錯誤代碼。
?
?????在Windows?sockets應(yīng)用程序中,當(dāng)WindowProc接收到網(wǎng)絡(luò)消息時,在該函數(shù)內(nèi)執(zhí)行下面的步驟:
?????1:讀取lParam的高字節(jié),判斷是否有錯誤發(fā)生。可以使用WSAGETSElECTERROR宏。
?????2:如果沒有錯誤,讀取lParam的低字節(jié),檢查發(fā)生了什么網(wǎng)絡(luò)事件,可以使用WSAGETSELECTEVENT宏。
?
?????WSAGETSElECTERROR和WSAGETSELECTEVENT宏定義如下:
?
[cpp]?view plaincopy
?
接下來就需要創(chuàng)建窗口和將網(wǎng)絡(luò)消息與消息處理函數(shù)關(guān)聯(lián)起來。如果使用MFC可以使用MFC提供的宏來進(jìn)行處理。
注意:多次調(diào)用WSAAsyncSelect時,最后一次調(diào)用會取消前面注冊的網(wǎng)絡(luò)事件。
?
因為調(diào)用accept接受的套接字和監(jiān)聽套接字具有同樣的屬性。所以,任何為監(jiān)聽套接字設(shè)置的網(wǎng)絡(luò)事件對接受套接字同樣起作用。如果一個監(jiān)聽套接字請求FD_ACCEPT、FD_READ和FD_WRITE網(wǎng)絡(luò)事件。則在該監(jiān)聽套接字上接受的任何套接字也會請求FD_ACCEPT,FD_READ和FD_WRITE網(wǎng)絡(luò)事件。
?
FD_CLOSE網(wǎng)絡(luò)事件用來判斷套接字是否已經(jīng)關(guān)閉。錯誤代碼會指出套接字是從容關(guān)閉還是硬關(guān)閉。如果為0,為從容關(guān)閉。若錯誤代碼為WSAECONNRESET,則套接字是硬關(guān)閉。調(diào)用closesocket不會投遞FD_CLOSE事件。
發(fā)生網(wǎng)絡(luò)事件的條件。
?
下列條件下會發(fā)生FD_READ事件:
1:當(dāng)調(diào)用WSAAsyncSelect函數(shù)時,如果當(dāng)前有數(shù)據(jù)可讀。
2:當(dāng)數(shù)據(jù)到達(dá)并且沒有發(fā)送FD_READ網(wǎng)絡(luò)事件時。
3:調(diào)用recv()或這recvfrom,如果仍有數(shù)據(jù)可讀里。
?
下列情況下會發(fā)生FD_WRITE事件:
1:調(diào)用WSAAsyncSelect函數(shù)時,如果能夠發(fā)送數(shù)據(jù)時。
2:connect或者accept函數(shù)后,連接已經(jīng)建立時。
3:調(diào)用send或者sendto函數(shù),返回WSAWOULDBLOCK錯誤后,再次調(diào)用send()或者sendto函數(shù)可能成功時。因為此時可能是套接字還處于不可寫狀態(tài),多次調(diào)用直到調(diào)用成功為止。
?
WSAAsyncSelect的優(yōu)勢與不足。
該模型是在基于消息的Windows環(huán)境下開發(fā)應(yīng)用程序。開發(fā)人員可以像處理其他消息一樣,對網(wǎng)絡(luò)事件進(jìn)行處理。而且為確保接受所有數(shù)據(jù)提供了很好的機制。
不足:由于該模型基于Windows消息機制,必須在應(yīng)用程序中創(chuàng)建窗口。雖然可以在開發(fā)中,確定是否顯示該窗口。?由于調(diào)用WSAAsyncSelect函數(shù)后自動將套接字設(shè)置為非阻塞狀態(tài),當(dāng)應(yīng)用程序接收到網(wǎng)絡(luò)事件時,未必能夠成功返回。這無疑增加了使用該模型的難度。
接下來展示一個使用如何WSAAsyncSelect模型的例子。該程序使用WSAAsyncSelect模型管理接受的客戶端套接字。編碼步驟如下:
?????1:聲明自定義消息。如WM_SOCKET
?????2:聲明窗口例程。
?????3:將自定義消息與消息處理函數(shù)相關(guān)聯(lián)。
?????4:初始化套接字動態(tài)庫,創(chuàng)建套接字。
?????5:調(diào)用WSAAsyncSelect注冊感興趣的網(wǎng)絡(luò)事件。本例服務(wù)器感興趣的網(wǎng)絡(luò)事件有FD_ACCEPT和FD_CLOSE。
?????6:綁定套接字開始監(jiān)聽。
?????一:聲明自定義消息:
?
[cpp]?view plaincopy?????除了聲明自定義消息外還需要聲明最大字符串長度、服務(wù)器監(jiān)聽端口、數(shù)據(jù)緩沖區(qū)。
?
[cpp]?view plaincopy?????二:聲明消息處理函數(shù)并與消息關(guān)聯(lián):
?
?????1:在窗口類頭文件中聲明消息處理函數(shù)。如:
?
[cpp]?view plaincopy?
?????2:在消息映射宏中將自定義消息如聲明的消息處理函數(shù)關(guān)聯(lián):
?
[cpp]?view plaincopy?
?????3:實現(xiàn)消息處理函數(shù):
[cpp]?view plaincopy
?
三:創(chuàng)建套接字并注冊感興趣的網(wǎng)絡(luò)事件.
1:初始化套接字動態(tài)庫,并創(chuàng)建套接字。
[cpp]?view plaincopy2:注冊感興趣的網(wǎng)絡(luò)事件:
[cpp]?view plaincopy?
3:綁定套接字并監(jiān)聽。
[cpp]?view plaincopy?
四:退出
[cpp]?view plaincopy
五:CClient類。
自定義類CClient類用于管理服務(wù)器接受客戶端的新建套接字。在該類中實現(xiàn)與客戶端通信。
?
六;管理客戶端套接字鏈表。
當(dāng)服務(wù)器接受一個客戶端連接請求后就會創(chuàng)建一個CClient實例。將該實例地址加入鏈表中。
===============================================================================================================
//
/// WSAAsyncEvent模型(同步I/O模型)
///這里為什么說他是同步的,就是因為實際的數(shù)據(jù)的Copy是同步進(jìn)行///的,而不是異步的,只是相應(yīng)的通知機制(通知數(shù)據(jù)已經(jīng)準(zhǔn)備好了),///是異步的
/// 和WSAAsyncSelect模型類似的是,它也允許應(yīng)用程序在一個或多個套接字上,接收以事件為
/// 基礎(chǔ)的網(wǎng)絡(luò)事件通知。在用新模型開發(fā)的應(yīng)用程序中,也能接收和處理所有那些事件。
/// 該模型最主要的差別在于網(wǎng)絡(luò)事件會投遞至一個事件對象句柄,而非投遞至一個窗口例程。
/// 它的基本思想是將每個套接字都和一個WSAEVENT對象對應(yīng)起來,并且在關(guān)聯(lián)的時候指定需要
/// 關(guān)注的哪些網(wǎng)絡(luò)事件。一旦在某個套接字上發(fā)生了我們關(guān)注的事件(FD_READ和FD_CLOSE),
/// 與之相關(guān)聯(lián)的WSAEVENT對象被Signaled。
/// 程序定義了兩個全局?jǐn)?shù)組,一個套接字?jǐn)?shù)組,一個WSAEVENT對象數(shù)組,其大小都是MAXIMUM_WAIT_OBJECTS(64),
/// 兩個數(shù)組中的元素一一對應(yīng)。
//
/// WSAEventSelect function specifies an event object to be associated with the supplied set of
/// FD_XXX network events.
//
/// 附加裝置:事件監(jiān)視器的集合,每一個事件監(jiān)視器監(jiān)視一個Socket上的相應(yīng)的相應(yīng)的行為
/// 微軟的信箱非常暢銷,購買微軟信箱的人以百萬計數(shù)......以至于蓋茨每天24小時給客戶打電話
/// ,累得腰酸背痛,喝蟻力神都不好使。微軟改進(jìn)了他們的信箱:在客戶的家中添加一個附加裝置
/// ,這個裝置會監(jiān)視客戶的信箱,每當(dāng)新的信件來臨,此裝置會發(fā)出“新信件到達(dá)”聲,提醒老陳
/// 去收信。蓋茨終于可以睡覺了。??
//
| #include?<winsock2.h> |
總結(jié)
以上是生活随笔為你收集整理的详细解析WSAAsyncSelect模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用完成例程(Completion Rou
- 下一篇: 详细解析WSAEventSelect模型