簡述:
通知套接口有請求事件發生.#include <winsock.h>
int PASCAL FAR WSAAsyncSelect ( SOCKET s, HWND hWnd,
unsigned int wMsg,long lEvent );
s 標識一個需要事件通知的套接口的描述符. hWnd 標識一個在網路事件發生時需要接收訊息的視窗句柄.
wMsg 在網路事件發生時要接收的訊息.
lEvent位禁止碼,用於指明應用程式感興趣的網路事件集合.
注釋:
本函式用來請求Windows Sockets DLL為視窗句柄發一條訊息-無論它何時檢測到由lEvent參數指明的網路事件.要傳送的訊息由wMsg參數標明.被通知的套接口由s標識.本函式自動將套接口設定為非阻塞模式.
lEvent參數由下表中列出的值組成.
值 意義
FD_READ 欲接收讀準備好的通知.
FD_WRITE 欲接收寫準備好的通知.
FD_OOB欲接收帶邊數據到達的通知.
FD_ACCEPT 欲接收將要連線的通知.
FD_CONNECT欲接收已連線好的通知.
FD_CLOSE 欲接收套接口關閉的通知.
啟動一個WSAAsyncSelect()將使為同一個套接口啟動的所有先前的WSAAsyncSelect()作廢. 例如,要接收讀寫通知,應用程式必須同時用FD_READ和FD_WRITE調用WSAAsyncSelect(),如下:
rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE);
對不同的事件區分不同的訊息是不可能的.下面的代碼將不會工作;第二個調用將會使第一次調用的作用失效,只有FD_WRITE會通過wMsg2訊息通知到.
rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);
rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);
如果要取消所有的通知,也就是指出Windows Sockets的實現不再在套接口上傳送任何和網路事件相關的訊息,則lEvent應置為0.
rc = WSAAsyncSelect(s, hWnd, 0, 0);
儘管在本例中,WSAAsyncSelect()立即使傳給該套接口的事件訊息無效, 仍有可能有訊息等在應用程式的訊息佇列中.應用程式因此也必須仍準備好接收網路訊息-即使訊息作廢.用closesocket()關閉一個套接口也同樣使WSAAsyncSelect()傳送的訊息作廢,但在closesocke()之前佇列中的訊息仍然起作用.
由於一個已調用accept()的套接口和用來接收它的偵聽套接口有同樣的屬性, 任何為偵聽套接口設定的的WSAAsyncSelect()事件也同樣對已接收的套接口起作用.例如, 如果一個偵聽的套接口有WSAAsyncSelect()事件FD_ACCEPT,FD_READ,FD_WRITE, 則任何在那個偵聽的套接口上接收的套接口將也有FD_ACCEPT,FD_READ,FD_WRITE事件,以及同樣的wMsg的值.若需要不同的wMsg及事件,應用程式應調用WSAAsyncSelect(),將已接收的套接口和想要傳送的新訊息作為參數傳遞.
當某一套接口s上發生了一個已命名的網路事件,應用程式視窗hWnd會接收到訊息wMsg.wParam參數標識了網路事件發生的套接口.lParam的低字指明了發生的網路事件.lParam的高字則含有一個錯誤代碼.該錯誤代碼可以是winsock.h中定義的任何錯誤.
錯誤代碼和事件可以通過WSAGETSELECTERRORH和WSAGETSELECTEVENT宏從lParam中取出.定義如下:
#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
注意:在accept()調用和為改變事件或wMsg的WSAAsyncSelect()調用中有一個計時視窗.應用程式如果需要給偵聽的和調用過accept()的套接口以不同的wMsg,它就應該在偵聽的套接口上請求FD_ACCEPT事件,然後在accept()調用後設定相應的事件.由於FD_ACCEPT從不傳送給已連線的套接口,而FD_READ,FD_WRITE,FD_OOB及FD_CLOSE也從不傳送給偵聽套接口,所以不會產生困難.
使用以上的宏將最大限度的提高應用程式的可移植性.
返回的可能網路事件如下:
值 意義
FD_READ 套接口s準備讀
FD_WRITE 套接口s準備寫
FD_OOB帶外數據準備好在套接口s上讀.
FD_ACCEPT 套接口s準備接收新的將要到來的連線.
FD_CONNECT套接口s上的連線完成.
FD_CLOSE 由套接口s標識的連線已關閉.
返回值:
0 若應用程式感興趣的網路事件的聲明成功.
SOCKET_ERROR 否則.可通過調用WSAGetLastError()返回特定的錯誤代碼.
評價:
儘管WSAAsyncSelect()可以以多個事件的組合來調用,應用程式視窗還是會為每個網路事件接收一條訊息.
如同select()函式,WSAAsyncSelect()會被頻繁地調用來決定,何時一次數據轉移操作(send()或recv())可以啟動,並且可以立刻成功.儘管如此,健壯的應用程式必須做好這樣的準備, 即它可能接收到訊息及啟動了一個會立即返回WSAEWOULDBLOCK的Windows Sockets API調用.例如,下列的事件序列是可能的:
(i) 數據到達套接口s;Windows Sockets傳遞WSAAsyncSelect訊息.
(ii) 應用程式處理其它一些訊息.
(iii) 在處理過程中,應用程式啟動了ioctlsocket(s,FIONREAD...)並且注意到有數據準備好讀.
(iv) 應用程式啟動recv(s,...)來讀數據.
(v) 應用程式循環處理下一條訊息,最終到達WSAAsyncSelect訊息,表示數據已準備好讀.
(vi) 應用程式啟動recv(s,...),但失敗並有錯誤WSAEWOULDBLOCK.
其它的事件序列也是可能的.
Windows Sockets DLL不會不斷地為某一特定的網路事件向一個應用程式傳送訊息. 如果已成功地向應用程式視窗傳送了一特定事件的通知,對該應用程式視窗將不再為該網路事件發訊息,直到應用程式調用函式隱含地重新通知該網路事件.
事件 重新通知函式
FD_READ recv()或recvfrom()
FD_WRITE send()或sendto()
FD_OOBrecv()
FD_ACCEPT accept()
FD_CONNECT無
FD_CLOSE 無
任何對重新通知函式的調用,即使失敗,也會達到為相關事件發重新通知訊息的效果.
對FD_READ,FD_OOB和FD_ACCEPT事件,訊息傳遞是"水平觸發"(level-triggered)的.這意味著,若調用了重新通知函式並且相關的事件對該調用仍有效,WSAAsyncSelect()訊息就將傳給應用程式.這為應用程式提供了事件驅動以及不必考慮在任一時刻到達的數據量的能力.考慮下列序列:
(i) Windows Sockets DLL在套接口s上接收100位元組的數據並傳遞一個FD_READ訊息.
(ii) 應用程式啟動recv(s,buffptr,50,0)接收50位元組.
(iii) 由於仍有數據未讀,Windows Sockets DLL傳送另一個FD_READ訊息.
根據以上語義,應用程式不必在收到FD_READ訊息時讀進所有可讀的數據-對應於每一FD_READ訊息進行一次recv()調用是恰當的.如果應用程式為一個FD_READ訊息而啟動了多個recv()調用,它將接收到多個FD_READ訊息.這樣的應用程式可能希望在開始recv()調用( 通過不為FD_READ事件置位的WSAAsyncSelect()函式調用)之前關閉FD_READ訊息.
如果在應用程式初次調用WSAAsyncSelect()或當調用了重新通知函式時,有一個事件為真, 則會傳送一個相應的訊息.例如,若應用程式調用listen(),就會試圖進行連線,然後應用程式調用WSAAsyncSelect()聲明它需要為套接口接收FD_ACCEPT訊息,Windows Sockets的實現就會立即傳遞一個FD_ACCEPT訊息.
FD_WRITE事件處理起來稍有不同.FD_WRITE訊息是在套接口第一次用connect()連線或由accept()接受,並且在send()或sendto()以WSAWOULDBLOCK錯誤失敗後緩衝區空閒時傳送的.因此,應用程式可以假設傳送可能在第一次FD_WRITE訊息時開始,並持續到一次返回WSAEWOULDBLOCK的傳送. 在這樣的失敗後,應用程式將被通知,FD_WRITE訊息的傳送又將可能.
FD_OOB事件只用在當套接口配置成獨立接收帶外數據時.如果一個套接口被配置成接收感興趣的帶外數據狀態,帶外數據將和普通數據等同視之,並且應用程式應該註冊它感興趣的方面,然後將接收FD_READ事件,而不是FD_OOB事件.應用程式可以設定或監控帶外數據處理的方法(通過使用setsockopt()或GETSOCKOPT()函式,及SO_OOBINLINE選項).
在FD_CLOSE訊息中的錯誤代碼指出套接口的關閉是正常的還是異常的.如果錯誤代碼是0,則關閉是正常的;若錯誤代碼是WSAECONNRESET,則套接口的虛套接口將被重置.這些只對SOCK_STREAM類型的套接口起作用.
FD_CLOSE訊息在相應套接口的虛電路關閉指令接收到時傳送.在TCP術語中,這意味著FD_CLOSE在連線進入了FIN WAIT或CLOSE WAIT狀態時傳送.這是遠端對傳送方進行了shutdown()調用或closesocket()調用的結果.
請注意你的應用程式將只會收到FD_CLOSE訊息來指出虛電路的關閉.它不會收到FD_READ訊息來表示該狀況.
錯誤代碼:
WSANOTINITIALISED 在使用本API前必須進行一次成功的WSAStartup()調用.
WSAENETDOWN WINDOWS SOCKETS實現已檢測到網路子系統故障.
WSAEINVAL 指出指定的參數之一是非法的.
WSAEINPROGRESS 一個阻塞的Windows Sockets操作正在進行.
附加的錯誤代碼可能在應用程式視窗接收到訊息時被置.這些代碼可以用WSAGETSELECTERROR宏從lParam中取出.對應於每個網路事件的可能錯誤代碼為:
事件:FD_CONNECT
WSAEADDRINUSE 給定的地址已被使用.
WSAEADDRNOTAVAIL 指定的地址在本地機器不能使用.
WSAEAFNOSUPPORT 指定族的地址不能和本套接口同時使用.
WSAECONNREFUSED 連線的嘗試被拒絕.
WSAEDESTADDRREQ 需要一個目的地址.
WSAEFAULT namelen參數不正確.
WSAEINVAL 套接口已經約束到一個地址.
WSAEISCONN 套接口已經連線.
WSAEMFILE 沒有可用的檔案描述符.
WSAENETUNREACH 此時網路不能從該主機訪問.
WSAENOBUFS 無可用的緩衝區空間.套接口不能連線.
WSAENOTCONN 套接口沒有連線.
WSAENOTSOCK 該描述符是檔案,不是套接口.
WSAETIMEDOUT 試圖連線逾時,未建立連線.
事件:FD_CLOSE
WSAENETDOWN WINDOWS SOCKETS實現已檢測到網路子系統故障.
WSAECONNRESET 連線由遠端重建.
WSAECONNABORTED 由於逾時或其它失敗放棄連線.
事件:FD_READ
事件:FD_WRITE
事件:FD_OOB
事件:FD_ACCEPT
WSAENETDOWN WINDOWS SOCKETS實現已檢測到網路子系統故障.
關於Windows Sockets提供者的說明:
Windows Sockets的提供者應確保訊息可以成功地傳給應用程式.如果PostMessag()操作失敗,Windows Sockets的實現必須重發該訊息-只要視窗存在.
Windows Sockets提供者應使用WSAMAKESELECTREPLY宏來構造訊息中的lParam參數.
當套接口關閉時,Windows Sockets提供者應清除所有保留下來要傳送給應用程式視窗的訊息.然而應用程式必須準備好接收,放棄任何在closesocket()之前可能已經傳送的訊息.
參見:
select()