WSAASYNCSELECT模型

Winsock提供了一個有用的異步I/O模型。利用這個模型,應用程式可在一個套接字上,接收以Windows訊息為基礎的網路事件通知。具體的做法是在建好一個套接字後,調用WSAAsyncSelect函式。

基本介紹

  • 中文名:WSAASYNCSELECT模型
  • 屬性:異步I/O模型
  • 作用:接收網路事件通知
  • 調用函式:調用WSAAsyncSelect函式
異步通知,但不提供異步數據傳送。異步數據傳送有“重疊及完成連線埠模型提供”。
要想使用WSAAsyncSelect模型,在應用程式中,首先必須用CreateWindow函式創建一個視窗,再為該視窗提供一個視窗例程支持函式(Winproc)。亦可使用一個對話框,為其提供一個對話例程,而非視窗例程,因為對話框本質也是“視窗”。
intWSAAsyncSelect(
SOCKETs,//要進行操作的SOCKET
HWNDhWnd,//要綁寫的視窗句柄(當事件發生後要接收訊息的視窗)
unsignedintwMsg,//網路事件發生時的的訊息回響
longlEvent//感興趣的網路事件,請查閱MSDN
);使用方法可分為:
(1)初始化套接字相關信息:
(2)開始啟動一個事件通知。WSAAsyncSelect(Sock,hWnd,自定義訊息,網路事件)
(3)回響視窗的自定義訊息處理函式, 其中lparam的高位字包含了可能出現的錯誤
代碼,低位元組表示發生的網路事件。wParam表示發生網路事件的套接字。
WSAGETSELECTERROR(lParam);//查看是否出現錯誤,獲取低位元組位
WSAGETSELECTEVENT(lParam);//查看發生了什麼事件,獲取高位元組位
事件種類請查看MSDN,可用WSAGetLastError()來獲取錯誤信息。
注意:多個事件必須一次註冊完成,closesocketWSAAsyncSelect
優點:可在系統開銷不大的情況下同時處理許多連線。
缺點:即使用不需要視窗(如伺服器,控制台)它也不得不額外使用一個視窗。同時如果處理成千上萬套接字的所有事件,性能可想而知。
MFC的CSocket所使用的正是這種事件通知模型
今天看了一下,訊息通知的幾個類型。用的比較多的可能就是FD_SEND,FD_WRITE,在網上查了一下資料,總結如下:
【FD_WRITE事件】
(1)呼叫WSAAsyncSelect()來設定FD_WRITE事件時,Socket已經可以傳送資料(TCPscoket已經和對方連線成功了,或UDPsocket已建立完成),且目前outputbuffer仍有空間可寫入資料。
(2)呼叫WSAAsyncSelect()來設定FD_WRITE事件時,Socket尚不能傳送資料,不過一旦Socket與對方連線成功,馬上就會收到FD_WRITE的通知。
(3)呼叫send()或sendto()傳送資料時,系統告知錯誤,且錯誤碼為10035WSAEWOULDBLOCK(呼叫WSAGetLastError()得知這項錯誤),這時表示outputbuffer已經滿了,無法再寫入任何資料(此時即令呼叫再多次的send()也都一定失敗);一旦系統將部份資料成功送抵對方,空出outputbuffer後,便會送一個FD_WRITE給使用者,告知可繼續傳送資料了。換句話說,讀者在呼叫send()傳送資料時,只要不是返回錯誤10035的話,便可一直繼續呼叫send()來傳送資料;一旦send()回返錯誤10035,那麽便不要再呼叫send()傳送資料,而須等收到FD_WRITE後,再繼續傳送資料。
如果只是傳送很少的數據,可能只出現第二種情況,所以在傳送少量數據的時候,不要使用FD_WRITE機制。
【FD_READ事件】
(1)呼叫WSAAsyncSelect函式來對此Socket設定FD_READ事件時,inputbuffer中已有資料。
(2)原先系統的inputbuffer是空的,當系統再收到資料時,會通知我們。
(3)使用者呼叫recv或recvfrom函式,從inputbuffer讀取資料,但是並沒有一次將資料讀光,此時會再驅動一個FD_READ事件,表示仍有資料在inputbuffer中。
讀者必須注意:如果我們收到FD_READ事件通知的訊息,但是我們故意不呼叫recv或recvfrom來讀取資料的話,爾後系統又收到資料時,並不會再次通知我們,一定要等我們呼叫了recv或recvfrom後,才有可能再收到FD_READ的事件通知。
下面是一個利用WSAAsyncSelect的小程式:
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD(2,2);
err=WSASTARTUP(wVersionRequested,&wsaData);
if(err!=0)
{
return;
}
if(LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
return;
}
charhostname;
gethostname(hostname,sizeof(hostname));//這一代碼是為
hostent*hos=gethostbyname(hostname);//了實現自動獲取安
CStringCS=inet_ntoa(*(structin_addr*)hos->h_addr_list);//裝程式的主機代碼
SOCKETserverSocket;
serverSocket=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_INaddr;
addr.sin_family=AF_INET;
addr.sin_port=htons(6000);
addr.sin_addr.S_un.S_addr=inet_addr(CS);
bind(serverSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR));
//註冊網路事件
if(SOCKET_ERROR==WSAAsyncSelect(serverSocket,m_hWnd,WM_NETMESSAGE,FD_ACCEPT|FD_CLOSE))
{
MessageBox("註冊事件出錯!");
closesocket(serverSocket);
WSACleanup();
return;
}
listen(serverSocket,5);
}
voidCAsyselectsrvDlg::Onrecvmessage(WPARAMwParam,LPARAMlParam)
{
if(WSAGETSELECTERROR(lParam))
{
AfxMessageBox("接受訊息錯誤");
closesocket(wParam);
return;
}
switch(WSAGETSELECTEVENT(lParam))
{
caseFD_ACCEPT:
acceptclient(wParam,m_hWnd);
break;
caseFD_WRITE:
MessageBox("write");
break;
caseFD_READ:
MessageBox("read");
break;
caseFD_CLOSE:
closesocket(wParam);
break;
default:
break;
}
return;
}
voidacceptclient(SOCKETsock,HWNDhwnd)
{
SOCKETacceptclient;
SOCKADDR_INclientaddr;
intlen=sizeof(SOCKADDR_IN);
acceptclient=accept(sock,(structsockaddr*)&clientaddr,&len);
if(SOCKET_ERROR==WSAAsyncSelect(acceptclient,hwnd,WM_NETMESSAGE,FD_READ|FD_WRITE|FD_CLOSE))
{
AfxMessageBox("註冊訊息出錯!");
closesocket(sock);
closesocket(acceptclient);
WSACleanup();
return;
}
}
這段程式可以正常運行,前面定義全局函式:voidacceptclient(SOCKETsock,HWNDhwnd);

相關詞條

熱門詞條

聯絡我們