原文连接地址:http://www.javashuo.com/article/p-mvmlmoeg-v.htmlhtml
WSAAsyncSelect模型也称异步选择模型,其核心函数是WSAAsyncSelect。它能够用来在一个socket上接收以windows消息为基础的网络事件。它提供了读写数据的异步通知功能,但不提供异步数据传送。WSAAsyncSelect模型的优点在于只须要一个主线程便可。缺点是必需要绑定窗口句柄。 windows
Description:The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.网络
int WSAAsyncSelect( __in SOCKET s, __in HWND hWnd, __in unsigned int wMsg, __in long lEvent );
A descriptor that identifies the socket for which event notification is required. app
A handle that identifies the window that will receive a message when a network event occurs. less
接收网络事件消息的窗口句柄 异步
A message to be received when a network event occurs. socket
网络事件消息 async
A bitmask that specifies a combination of network events in which the application is interested. ide
网络事件,windows已定义好网络事件,如FD_CONNECT、FD_CLOSE、FD_ACCEPT等。函数
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.
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:
#define WSAGETSELECTERROR(lParam) HIWORD(lParam) #define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
自定义消息函数的传递参数中,wParam 标识socket描述符。lParam 的低字节标识了网络事件,lParam 的高字节标识了错误码。分别用WSAGETSELECTEVENT和WSAGETSELECTERROR能够取出lparam内的网络事件和错误码。
WSAAsyncSelect 传参须要窗口句柄,为了简化代码,我直接建立了一个mfc对话框程序,用m_hwnd给WSAAsyncSelect 传参。对话框类名为WSAAsyncSelecDlg。
A:定义变量
bool m_bRes; //用做socket流程各函数调用依据 WSAData m_wsa; //wsastartup参数 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
m_bRes = true; WSAStartup(MAKEWORD(2,3),&m_wsa); m_listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (m_listensocket == INVALID_SOCKET ) { m_bRes = false; } sockaddr_in m_server; m_server.sin_family = AF_INET; m_server.sin_port = htons(8828); m_server.sin_addr.s_addr = inet_addr("127.0.0.1"); if (m_bRes && (bind(m_listensocket,(sockaddr*)&m_server,sizeof(sockaddr_in)) == SOCKET_ERROR)) { DWORD dw = WSAGetLastError(); m_bRes = false; } if (m_bRes && (listen(m_listensocket,SOMAXCONN) == SOCKET_ERROR)) { m_bRes = false; } if (m_bRes && (WSAAsyncSelect(m_listensocket,m_hWnd,WM_SOCK,FD_ACCEPT) == SOCKET_ERROR)) { m_bRes = false; }
D:实现消息映射函数
LRESULT CWSAAsyncSelecDlg::OnSocket(WPARAM w,LPARAM l) { SOCKET s = (SOCKET)w; switch (WSAGETSELECTEVENT(l)) { case FD_ACCEPT: {//有网络链接到达 sockaddr_in m_client; int sz = sizeof(sockaddr_in); SOCKET acp = accept(m_listensocket,(sockaddr*)&m_client,&sz); if (acp == INVALID_SOCKET) { closesocket(m_listensocket); return 0; } WSAAsyncSelect(acp,m_hWnd,WM_SOCK,FD_READ|FD_WRITE|FD_CLOSE); } break; case FD_READ: {//缓冲区有数据待接收时进入 char buf[1024]; int res = recv(s,buf,1024,0); if (res == 0) { closesocket(s); break; } else if (res == SOCKET_ERROR) { //socket error break; } else { buf[res] = 0; std::string str = buf; str += "\n"; OutputDebugString(str.c_str()); str = "WSAAsyncSelect test"; int res = send(s,str.c_str(),str.length(),0); if (res == SOCKET_ERROR) { break; } } } break; case FD_WRITE: {//1:新链接到达时进入 2:缓冲区满数据未发送彻底时进入 std::string str = "WSAAsyncSelect test"; int res = send(s,str.c_str(),str.length(),0); if (res == SOCKET_ERROR) { break; } } break; case FD_CLOSE: {//客户端关闭链接时进入 closesocket(s); } break; } return 1; }
E:测试结果
客户端程序用的是http://www.cnblogs.com/hgwang/p/6086237.html里面的代码。