WSAAsyncSelect模型也稱異步選擇模型,其核心函數是WSAAsyncSelect。它能夠用來在一個socket上接收以windows消息爲基礎的網絡事件。它提供了讀寫數據的異步通知功能,但不提供異步數據傳送。WSAAsyncSelect模型的優點在於只須要一個主線程便可。缺點是必需要綁定窗口句柄。html
Description:The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.編程
1 int WSAAsyncSelect( 2 __in SOCKET s, 3 __in HWND hWnd, 4 __in unsigned int wMsg, 5 __in long lEvent 6 );
A descriptor that identifies the socket for which event notification is required.windows
A handle that identifies the window that will receive a message when a network event occurs.網絡
接收網絡事件消息的窗口句柄app
A message to be received when a network event occurs.less
網絡事件消息異步
A bitmask that specifies a combination of network events in which the application is interested.socket
網絡事件,windows已定義好網絡事件,如FD_CONNECT、FD_CLOSE、FD_ACCEPT等。async
If the WSAAsyncSelect function succeeds, the return value is zero, provided that the application's declaration of interest in the network event set was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.ide
MSDN上列出以下常見網絡事件:
MSDN對上述網絡事件及關聯函數觸發關係的解釋以下:
Here is a summary of events and conditions for each asynchronous notification message.
Note When setsockopt SO_OOBINLINE is enabled, data includes both normal data and OOB data in the instances noted above.
Note FD_CLOSE is not posted after closesocket is called.---- 獲取FD_CLOS後應調用closesocket
連續調用兩次WSAAsyncSelect函數,後設置的事件標誌位將替換先前設置的事件標誌位。設置多重事件,須要用到或運算,如FD_READ|FD_WRITE。
The wParam parameter identifies the socket on which a network event has occurred. The low word of lParam specifies the network event that has occurred. The high word of lParam contains any error code. The error code be any error as defined in Winsock2.h.
WSAGETSELECTEVENT和WSAGETSELECTERROR宏
Description:The error and event codes can be extracted from the lParam using the macros WSAGETSELECTERROR and WSAGETSELECTEVENT, defined in Winsock2.h as:
1 #define WSAGETSELECTERROR(lParam) HIWORD(lParam) 2 #define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
自定義消息函數的傳遞參數中,wParam 標識socket描述符。lParam 的低字節標識了網絡事件,lParam 的高字節標識了錯誤碼。分別用WSAGETSELECTEVENT和WSAGETSELECTERROR能夠取出lparam內的網絡事件和錯誤碼。
WSAAsyncSelect 傳參須要窗口句柄,爲了簡化代碼,我直接建立了一個mfc對話框程序,用m_hwnd給WSAAsyncSelect 傳參。對話框類名爲WSAAsyncSelecDlg。
A:定義變量
1 bool m_bRes; //用做socket流程各函數調用依據 2 WSAData m_wsa; //wsastartup參數 3 SOCKET m_listensocket; //監聽socket
B:定義消息宏及消息映射函數
#define WM_SOCK WM_USER+1 afx_msg LRESULT OnSocket(WPARAM w,LPARAM l); ON_MESSAGE(WM_SOCK,OnSocket)
C:在OnInitDialog內建立監聽socket
1 m_bRes = true; 2 WSAStartup(MAKEWORD(2,3),&m_wsa); 3 m_listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 4 if (m_listensocket == INVALID_SOCKET ) 5 { 6 m_bRes = false; 7 } 8 sockaddr_in m_server; 9 m_server.sin_family = AF_INET; 10 m_server.sin_port = htons(8828); 11 m_server.sin_addr.s_addr = inet_addr("127.0.0.1"); 12 if (m_bRes && (bind(m_listensocket,(sockaddr*)&m_server,sizeof(sockaddr_in)) == SOCKET_ERROR)) 13 { 14 DWORD dw = WSAGetLastError(); 15 m_bRes = false; 16 } 17 if (m_bRes && (listen(m_listensocket,SOMAXCONN) == SOCKET_ERROR)) 18 { 19 m_bRes = false; 20 } 21 if (m_bRes && (WSAAsyncSelect(m_listensocket,m_hWnd,WM_SOCK,FD_ACCEPT) == SOCKET_ERROR)) 22 { 23 m_bRes = false; 24 }
D:實現消息映射函數
1 LRESULT CWSAAsyncSelecDlg::OnSocket(WPARAM w,LPARAM l) 2 { 3 SOCKET s = (SOCKET)w; 4 switch (WSAGETSELECTEVENT(l)) 5 { 6 case FD_ACCEPT: 7 {//有網絡鏈接到達 8 sockaddr_in m_client; 9 int sz = sizeof(sockaddr_in); 10 SOCKET acp = accept(m_listensocket,(sockaddr*)&m_client,&sz); 11 if (acp == INVALID_SOCKET) 12 { 13 closesocket(m_listensocket); 14 return 0; 15 } 16 WSAAsyncSelect(acp,m_hWnd,WM_SOCK,FD_READ|FD_WRITE|FD_CLOSE); 17 } 18 break; 19 case FD_READ: 20 {//緩衝區有數據待接收時進入 21 char buf[1024]; 22 int res = recv(s,buf,1024,0); 23 if (res == 0) 24 { 25 closesocket(s); 26 break; 27 } 28 else if (res == SOCKET_ERROR) 29 { 30 //socket error 31 break; 32 } 33 else 34 { 35 buf[res] = 0; 36 std::string str = buf; 37 str += "\n"; 38 OutputDebugString(str.c_str()); 39 str = "WSAAsyncSelect test"; 40 int res = send(s,str.c_str(),str.length(),0); 41 if (res == SOCKET_ERROR) 42 { 43 break; 44 } 45 } 46 } 47 break; 48 case FD_WRITE: 49 {//1:新鏈接到達時進入 2:緩衝區滿數據未發送徹底時進入 50 std::string str = "WSAAsyncSelect test"; 51 int res = send(s,str.c_str(),str.length(),0); 52 if (res == SOCKET_ERROR) 53 { 54 break; 55 } 56 } 57 break; 58 case FD_CLOSE: 59 {//客戶端關閉鏈接時進入 60 closesocket(s); 61 } 62 break; 63 } 64 return 1; 65 }
E:測試結果
客戶端程序用的是http://www.cnblogs.com/hgwang/p/6086237.html裏面的代碼。