博客原文地址
WSAEventSelect模型是类似于WSAAsyncSelect模型的另一个有用的异步I/O模型。它允许应用程序在一个或者多个套接字上接收以事件为基础的网络事件。 在这里,最主要的差别是在于网络事件会投递到一个事件对象句柄。并不是投递到一个窗口。
我们使用事件模型前,我们的应用程序针对使用的每一个套接字首先要创建一个事件对象:
当我们用WSACreateEvent创建了事件,他有两种工作状态和两种工作模式:
1、工作状态:已传信signaled 和 未传信nonsignaled。
2、工作模式:人工重置manual reset 和 自动重置auto reset。
刚开始的时候,wsacreateevent是一种未传信的工作状态,并且是人工重置的模式来创建一个句柄。 但是我们网络事件触发了与一个套接字关联在一起的事件对象,工作状态便会从“未传信”转变为“已传信”,由于事件对象是 一种人工重置模式创建的,所以在完成了I/O请求处理后,我们的应用程序需要负责将工作状态改变为“未传信”。 要这样我们需要一个函数:
当我们完成一个事件对象的处理后,我们要调用WSACloseEvent函数,释放由事件句柄使用的系统资源:
当我们的一个套接字和一个事件句柄关联在一起后,应用程序就开始I/O处理,通过WSAWaitForMultipleEvents函数等待网络事件来出发事件对象句柄的工作状态。(这个函数用来等待一个或者多个事件对象句柄,并在事先指定的一个或所有句柄进入“已传信”状态后,并在超过一个规定的事件周期后立即返回)
通常应用程序应将这个参数设为FALSE,一次只为一个套接字事件提供服务。
第四个参数dwTimeout要注意的是尽量避免将超时值设为0, 加入没有等待处理的事件,WSAWaitForMultipleEvents便会返回WSA_WAIT_TIMEOUT。
你将这个参数设为:WSAINFINITE(永久等待),那么只有当在一个网络事件传信了一个事件对象后函数才会返回。
当WSAWaitForMultipleEvents 收到一个事件对象的网络事件通知,便会返回一个值。指出造成函数返回的事件对象。 我们的应用程序就可以引用事件数组中已传信的事件,并检索与那个事件对应的套接字,判断到底是在那个套接字上,发生了什么网络事件类型。对于事件数组中的事件进行引用时,应该用WSAWaitForMultipleEvents的返回值 减去预定义的WSA_WAIT_EVENT_0得到具体的引用值:
下面来看一下使用步骤:
1、建立一个socket。
2、建立一个event。
3、用WSAEventSelect将socket,和event关联起来。lNetworkEvents可以为 FD_ACCEPT ,FD_READ ,FD_WRITE,FD_CLOSE 等等.
4、等待事件句柄:index = WSAWaitForMultipleEvents(EventTotal,EventArray,FAlSE,100/*WSA_INFINITE*/,FALSE);
5、查询网络事件: int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject,LPWSANETWORKEVENTS lpNetworkEvents)
6、用lpNetworkEvents.lNetworkEvents & FD_ACCEPT ,lpNetworkEvents.lNetworkEvents & FD_CLOSE找到各个事件处理。
- #include <WinSock2.h>
- #include <iostream>
- using namespace std;
- #pragma comment(lib, "WS2_32.lib")
- int main()
- {
- WSAEVENT eventArray[1024];
- SOCKET sockArray[1024];
- int nEventTotal = 0;
- WSADATA wsadata;
- WORD sockVersion = MAKEWORD(2, 0);
- WSAStartup(sockVersion, &wsadata);
- SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(8888);
- sin.sin_addr.S_un.S_addr = INADDR_ANY;
- if (bind(s, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
- {
- cout << "error" << endl;
- return 0;
- }
- listen(s, 5);
- WSAEVENT event = ::WSACreateEvent();
- ::WSAEventSelect(s, event, FD_ACCEPT|FD_CLOSE);
- eventArray[nEventTotal] = event;
- sockArray[nEventTotal] = s;
- while(TRUE)
- {
- // 在所有事件对象上等待
- int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
- // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
- nIndex = nIndex - WSA_WAIT_EVENT_0;
- for(int i=nIndex; i<nEventTotal; i++)
- {
- nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);
- if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
- {
- continue;
- }
- else
- {
- // 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
- WSANETWORKEVENTS event;
- ::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);
- if(event.lNetworkEvents & FD_ACCEPT) // 处理FD_ACCEPT通知消息
- {
- if(event.iErrorCode[FD_ACCEPT_BIT] == 0)
- {
- if(nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
- {
- cout << " Too many connections! " << endl;
- continue;
- }
- SOCKET sNew = accept(sockArray[i], NULL, NULL);
- WSAEVENT event = ::WSACreateEvent();
- ::WSAEventSelect(sNew, event, FD_READ|FD_CLOSE|FD_WRITE);
- // 添加到表中
- eventArray[nEventTotal] = event;
- sockArray[nEventTotal] = sNew;
- nEventTotal++;
- }
- }
- else if(event.lNetworkEvents & FD_READ) // 处理FD_READ通知消息
- {
- if(event.iErrorCode[FD_READ_BIT] == 0)
- {
- char szText[256];
- int nRecv = ::recv(sockArray[i], szText, 256, 0);
- if(nRecv > 0)
- {
- szText[nRecv] = 0;
- cout << "接收到数据:" << szText << endl;
- }
- }
- }
- else if(event.lNetworkEvents & FD_CLOSE) // 处理FD_CLOSE通知消息
- {
- if(event.iErrorCode[FD_CLOSE_BIT] == 0)
- {
- ::closesocket(sockArray[i]);
- for(int j=i; j<nEventTotal-1; j++)
- {
- eventArray[j] = eventArray[j+1];
- sockArray[j] = sockArray[j+1];
- }
- nEventTotal--;
- i--;
- }
- }
- else if(event.lNetworkEvents & FD_WRITE) // 处理FD_WRITE通知消息
- {
- }
- }
- }
- }
- return 0;
- }