WsaAsyncSelect模型就是这样一个解决了普通select模型问题的socket编程模型。它是在有客户端数据到来时,系统发送消息给我们的程序,我们的程序只要定义好消息的处理方法就可以了,用到的函数只要是WSAAsyncSelect,如:
首先,我们定义一个Windows消息,告诉系统,当有客户端数据到来时,发送该消息给我们。
#define UM_SOCK_ASYNCRECVMSG WM_USER + 1
在我们的处理函数中可以如下监听客户端的连接:
SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(7788); sin.sin_addr.S_un.S_addr = INADDR_ANY; int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin))); if ( nRet == SOCKET_ERROR ) { DWORD errCode = GetLastError(); return; } listen( listenSock, 5); int clientNum = 0; sockaddr_in clientAddr; int nameLen = sizeof( clientAddr ); while( clientNum < FD_SETSIZE ) { SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen ); //hWnd为接收系统发送的消息的窗口句柄 WSAAsyncSelect( clientSock, hWnd, UM_SOCK_ASYNCRECVMSG, FD_READ | FD_CLOSE ); clientNum++; }
接下来,我们需要在我们的窗口添加对UM_SOCK_ASYNCRECVMSG消息的处理函数,在该函数中真正接收客户端发送过来的数据,在这个消息处理函数中的wparam参数表示的是客户端套接字,lparam参数表示的是发生的网络事件如:
SOCKET clientSock = (SOCKET)wParam; if ( WSAGETSELECTERROR( lParam ) ) { closesocket( clientSock ); return; } switch ( WSAGETSELECTEVENT( lParam ) ) { case FD_READ: { char recvBuffer[1024] = {'\0'}; int nRet = recv( clientSock, recvBuffer, 1024, 0 ); if ( nRet > 0 ) { szRecvMsg.AppendFormat(_T("Client %d Say:%s\r\n"), clientSock, recvBuffer ); } else { //client disconnect szRecvMsg.AppendFormat(_T("Client %d Disconnect!\r\n"), clientSock ); } } break; case FD_CLOSE: { closesocket( clientSock ); szRecvMsg.AppendFormat(_T("Client %d Disconnect!\r\n"), clientSock ); } break; }
可以看到WsaAsyncSelect模型是非常简单的模型,它解决了普通select模型的问题,但是它最大的缺点就是它只能用在windows程序上,因为它需要一个接收系统消息的窗口句柄,那么有没有一个模型既可以解决select模型的问题,又不限定只能是windows程序才能用呢?下面我们来看看WsaEventSelect模型。