二 非同步選擇模型(WSAAsyncSelect)

2021-06-05 22:42:59 字數 3912 閱讀 7623

█ 非同步選擇(wsaasyncselect)模型是乙個有用的非同步 i/o 模型。利用這個模型,應用程式可在乙個套接字上,

接收以 windows 訊息為基礎的網路事件通知。具體的做法是在建好乙個套接字後,呼叫wsaasyncselect函式。

該模型的核心即是wsaasyncselect函式。

█ 要想使用 wsaasyncselect 模型,在應用程式中,首先必須用createwindow函式建立乙個視窗,再為該視窗提供乙個視窗例程函式(winproc)。

█ wsaasyncselect 的函式原型如下:

int wsaasyncselect(

__in socket s,

__in hwnd hwnd,

__in unsigned int wmsg,

__in long levent

);

● s 引數指定的是我們感興趣的那個套接字。

● hwnd 引數指定乙個視窗控制代碼,它對應於網路事件發生之後,想要收到通知訊息的那個視窗。

● wmsg 引數指定在發生網路事件時,打算接收的訊息。該訊息會投遞到由hwnd視窗控制代碼指定的那個視窗。

(通常,應用程式需要將這個訊息設為比windows的wm_user大的乙個值,避免網路視窗訊息與系統預定義的標準視窗訊息發生混淆與衝突)

● levent 引數指定乙個位掩碼,對應於一系列網路事件的組合,大多數應用程式通常感興趣的網路事件型別包括: 

fd_read、fd_write、fd_accept、fd_connect、fd_close。當然,到底使用fd_accept,還是使用fd_connect型別,

要取決於應用程式的身份是客戶端,還是伺服器。如應用程式同時對多個網路事件有興趣,只需對各種型別執行一次簡單的按位or(或)運算,

然後將它們分配給levent就可以了,例如:

wsaasyncseltct(s, hwnd, wm_socket, fd_connect | fd_read | fd_write | fd_close);

解釋說明:我們的應用程式以後便可在套接字s上,接收到有關連線、傳送、接收以及套接字關閉這一系列網路事件的通知。

█ 注意 ①:

多個事件務必在套接字上一次註冊!

另外還要注意的是,一旦在某個套接字上允許了事件通知,那麼以後除非明確呼叫closesocket命令,

或者由應用程式針對那個套接字呼叫了wsaasyncselect,從而更改了註冊的網路事件型別,否則的話,

事件通知會永遠有效!若將levent引數設為0,效果相當於停止在套接字上進行的所有網路事件通知。

█ 注意 ②:

若應用程式針對乙個套接字呼叫了wsaasyncselect,那麼套接字的模式會從「鎖定」變成「非鎖定」。

這樣一來,如果呼叫了像wsarecv這樣的winsock函式,但當時卻並沒有資料可用,那麼必然會造成呼叫的失敗,並返回wsaewouldblock錯誤。

為防止這一點,應用程式應依賴於由wsaasyncselect的umsg引數指定的使用者自定義視窗訊息,來判斷網路事件型別何時在套接字上發生;而不應盲目地進行呼叫。

fd_read

應用程式想要接收有關是否可讀的通知,以便讀入資料

fd_write

應用程式想要接收有關是否可寫的通知,以便寫入資料

fd_accept

應用程式想接收與進入連線有關的通知

fd_connect

應用程式想接收與一次連線完成的通知

fd_close

應用程式想接收與套接字關閉的通知

█ 應用程式在乙個套接字上成功呼叫了wsaasyncselect之後,會在與hwnd視窗控制代碼對應的視窗例程中,以windows訊息的形式,接收網路事件通知。

視窗例程通常定義如下:

lresult callback windowproc( 

hwnd hwnd,

uint umsg,

wparam wparam,

lparam lparam

);

● hwnd 引數指定乙個視窗的控制代碼,對視窗例程的呼叫正是由那個視窗發出的。

● umsg 引數指定需要對哪些訊息進行處理。這裡我們感興趣的是wsaasyncselect呼叫中定義的訊息。

● wparam 引數指定在其上面發生了乙個網路事件的套接字。假若同時為這個視窗例程分配了多個套接字,這個引數的重要性便顯示出來了。

● lparam引數中,包含了兩方面重要的資訊。其中, lparam的低字(低位字)指定了已經發生的網路事件,而lparam的高字(高位字)包含了可能出現的任何錯誤**。

█ 步驟:網路事件訊息抵達乙個視窗例程後,應用程式首先應檢查lparam的高字位,以判斷是否在網路錯誤。

這裡有乙個特殊的巨集: wsagetselecterror,可用它返回高字位包含的錯誤資訊。

若應用程式發現套接字上沒有產生任何錯誤,接著便應調查到底是哪個網路事件型別,具體的做法便是讀取lparam低字位的內容。

此時可使用另乙個特殊的巨集:wsagetselectevent,用它返回lparam的低字部分。

█ 注意 ③:應用程式如何對 fd_write 事件通知進行處理。

只有在三種條件下,才會發出 fd_write 通知:

■ 使用 connect 或 wsaconnect,乙個套接字首次建立了連線。

■ 使用 accept 或 wsaaccept,套接字被接受以後。

■ 若 send、wsasend、sendto 或 wsasendto 操作失敗,返回了 wsaewouldblock 錯誤,而且緩衝區的空間變得可用。

因此,作為乙個應用程式,自收到首條 fd_write 訊息開始,便應認為自己必然能在乙個套接字上發出資料,

直至乙個send、wsasend、sendto 或 wsasendto 返回套接字錯誤 wsaewouldblock。

經過了這樣的失敗以後,要再用另一條 fd_write 通知應用程式再次傳送資料。

用例:

wsaasyncselect(pthis->m_socklisten, pthis->getsafehwnd(), wm_socket, fd_accept | fd_close);//wm_socket為自定義訊息#define wm_socket wm_user+100

//override視窗過程函式

lresult cserverdlg::windowproc(uint message, wparam wparam, lparam lparam)

break;

case wm_socket:

if (wsagetselecterror(lparam))

switch(wsagetselectevent(lparam))

m_sockclient = accept(wparam, null, null);

wsaasyncselect(m_sockclient, m_hwnd, wm_socket, fd_read|fd_write|fd_close);

++m_clientnums;

break;

}case fd_read:

;recv(wparam, (char *)szbuf, max_buf_size, 0);

showmsg(szbuf);

break;

}case fd_write:

wparam = wparam;

break;

case fd_close:

--m_clientnums;

closesocket(wparam);

break;

} default:break;

} return cdialog::windowproc(message, wparam, lparam);

}

非同步選擇模型

非同步選擇 wsaasyncselect 模型是乙個有用的非同步 i o 模型。利用這個模型,應用程式可在乙個套接字上,接收以 windows 訊息為基礎的網路事件通知。具體 的做法是在建好乙個套接字後,呼叫wsaasyncselect函式。該模型的核心即是wsaasyncselect函式。wsaa...

非同步選擇模型

非同步選擇 wsaasyncselect 模型是乙個有用的非同步 i o 模型。利用這個模型,應用程式可在乙個套接字上,接收以 windows 訊息為基礎的網路事件通知。具體 的做法是在建好乙個套接字後,呼叫wsaasyncselect函式。該模型的核心即是wsaasyncselect函式。wsaa...

socket模型 非同步事件選擇模型的正常退出

今天要用到非同步事件選擇模型,發現demo原型不能正常退出.除錯了一下,用wsasetevent waitforsingleobject 全域性標記搞定.如果以後要用到非同步事件選擇模型,在這個demo上直接加業務邏輯.srcasynceventselect.zip include stdafx.h...