一、什么是异步选择模型
异步选择(WSAAsyncSelect)模型是一个异步 I/O 模型。利用这个模型,应用程序可在一个套接字上,接收以 Windows 消息机制为基础的网络事件通知,开发者将socket注册到消息机制,当socket有事件(新的连接,新的数据,连接断开,可以写入)来时候。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。
该模型的核心即是WSAAsyncSelect函数,该函数是非阻塞的。
二、与select模型比较
相同点:
他们都可以对Windows套接字应用程序所使用的多个套接字进行有效的管理。
不同点:
1.WSAAsyncSelect模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续执行;
2.发生网络事件时,应用程序得到的通知方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。
3.WSAAsyncSelect模型应用在基于消息的Windos环境下,使用该模型时必须创建窗口。而Select模型广泛应用在Unix系统和Windows系统,使用该模型不需要创建窗口。
4.应用程序调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变套接字的工作方式
三、异步选择模型API函数
WSAAsyncSelect函数定义如下:
int WSAAsyncSelect( __in SOCKET s, //指定的是我们感兴趣的那个套接字。 __in HWND hWnd, //指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。 __in unsigned int wMsg, //指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。 __in long lEvent //指定一个位掩码,对应于一系列网络事件的组合 );
WSAAsyncSelect模型是Select模型的异步版本,在调用select()函数时,会发生阻塞现象。可以通过select()函数timeout参数,设置函数调用的阻塞时间。在设定的时间内,线程保持等待,直到其中一个或多个套接字满足可读可写的条件时,该函数返回。
四、异步选择模型缺陷
不适合高并发网络结构,因为是基于窗口消息机制,消息太多就处理速度较慢。
五、代码示例
创建windwos窗口项目:
//1.添加头文件 #include<WinSock2.h> #pragma comment(lib,"Ws2_32.lib") #define WM_MYSOCKMSG WM_USER+1 bool HandleData(SOCKET sockClient);//处理收发数据 //2.初始化WSAstartup WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ return 0; } /* Confirm that the WinSock DLL supports 1.1.*/ /* Note that if the DLL supports versions greater */ /* than 1.1 in addition to 1.1, it will still return */ /* 1.1 in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ WSACleanup( ); return 0; } //3.再处理消息处添加代码 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { //1) 创建socket SOCKET sockServer = socket( AF_INET, SOCK_STREAM, //流式 IPPROTO_TCP);//tcp协议 // 2) 绑定端口 sockaddr_in siServer; siServer.sin_family = AF_INET; siServer.sin_port = htons(9527); siServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer)); if (nRet == SOCKET_ERROR) { printf("绑定失败 "); return 0; } // 3) 监听 nRet = listen(sockServer, SOMAXCONN); if (nRet == SOCKET_ERROR) { printf("监听失败 "); return 0; } //4)接受连接 //这个socket只关系新连接和关闭时间 WSAAsyncSelect(sockServer, hWnd, WM_MYSOCKMSG, FD_ACCEPT | FD_CLOSE); break; } case WM_MYSOCKMSG: { SOCKET sock = (SOCKET)wParam; WORD wErrCode = WSAGETSELECTERROR(lParam); WORD wSelectEvent = WSAGETSELECTEVENT(lParam); switch (wSelectEvent) { case FD_ACCEPT: { sockaddr_in siClient; int nSize = sizeof(siClient); SOCKET sockClient = accept(sock, (sockaddr*)&siClient, &nSize); //为新的连接注册对应的网络事件 WSAAsyncSelect(sockClient, hWnd, WM_MYSOCKMSG, FD_READ | FD_CLOSE); break; } case FD_READ://数据来了 { HandleData(sock); //处理数据 break; } case FD_CLOSE://连接断开 { //将sock从网络事件中移除 closesocket(sock); break; } } break; } case WM_COMMAND: { int wmId = LOWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此处添加使用 hdc 的任何绘图代码... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } bool HandleData(SOCKET sockClient) { // 5) 收发数据 char aryBuff[MAXWORD] = { 0 }; int nRet = recv(sockClient, aryBuff, sizeof(aryBuff), 0); if (nRet == 0 || nRet == SOCKET_ERROR) { printf("接受数据失败 "); return false; } printf("收到数据: %s ", aryBuff); char szBuff[] = { "recv OK " }; nRet = send(sockClient, szBuff, sizeof(szBuff), 0); if (nRet == SOCKET_ERROR) { printf("数据发送失败 "); return false; } return true; }