/**
* @brief 创建一个手工重设的事件对象,该对象的初始状态是未受信。
* 返回的事件对象的句柄不能被子进程所继承。事件对象是未命名的。
*
* @return 成功的话,返回事件对象句柄;否则,返回WSA_INVALID_EVENT。
* */
WSAEVENT WSACreateEvent(void);
/**
* @brief 将一个事件对象与FD_XXX等网络事件相关联。
*
* @param s 套接字描述符
* @param hEventObject 事件对象句柄
* @param lNetworkEvents 网络事件FD_XXX的位掩码组合
*
* @return 调用成功时,返回0;否则,返回SOCKET_ERROR。
* */
int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
/**
* @brief 在一个或多个事件对象上等待。当所等待的事件对象受信,或者指定
* 的时间过去时,此函数返回。
*
* @param cEvents 事件对象句柄的个数
* @param lphEvents 指向一个事件对象句柄数组
* @param fWaitAll 是否等待所有事件对象变成受信状态
* @param dwTimeout 指定要等待的时间
* @param fAlertable 使用WSAEventSelect模型时可忽略,应设为FALSE
*
* @return 如果调用成功,将返回下列值:
* 1) WSA_WAIT_EVENT_0至WSA_WAIT_EVENT_0 + cEvents - 1;
* 2) WSA_WAIT_IO_COMPLETION
* 3) WSA_WAIT_TIMEOUT
* 失败的话,返回WSA_WAIT_FAILED。
* */
DWORD WSAWaitForMultipleEvents(DWORD cEvnets, const WSAEVENT *lphEvents,
BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable);
使用WSAWaitForMultipleEvents()的注意事项:
将fWaitAll设为FALSE后,如果同时有几个事件对象受信,此函数的返回值仅能指明一个,就是句柄数组中最前的那个。如果指明的这个事件对象总有网络事件发生,那么后面其他事件对象所关联的网络事件就得不到处理了。解决办法是,此函数返回后,对每个事件都再次调用它,以便确定其状态。
/**
* @brief 查看发生了什么网络事件
*
* @param s 套接字描述符
* @param hEventObject 事件对象句柄
* @param lpNetworkEvents 网络事件和相关的出错代码
*
* @return 成功,返回0;否则,返回SOCKET_ERROR。
* */
int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject,
LPWSANETWORKEVENTS lpNetworkEvents);
WSANETWORKEVENTS对象的定义如下:
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
}WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
iErrorCode参数是一个数组,它的每一个成员对应着一个网络事件的出错代码。可以用预定义标识FD_READ_BIT(=0)、FD_WRITE_BIT(=1)等来索引FD_READ、FD_WRITE等事件发生时的出错代码。FD_READ与FD_READ_BIT的关系如下:
FD_READ (1 << FD_READ_BIT)
其它网络事件以此类推。
二、EventSelect模型的流程 :
1. 创建事件对象;
2. 将事件对象与FD_XXX等网络事件相关联;
3. 等待事件对象处于受信状态(有事件发生或者超时);
4. 枚举网络事件(确定什么事件发生了),编写事件处理代码。
附: 几个标识的含义
SOCKET —— typedef UINT_PTR(无符号整型数,32或64位)
WSAEVENT —— HANDLE (句柄)
WSA_WAIT_EVENT_0 —— ((DWORD)0)
WSA_WAIT_TIMEOUT —— ((DWORD)0x102L)
WSA_WAIT_FAILED —— ((DWORD)-1L)
参考文献
1.《Windows网络与通信程序设计》(王艳平);
2. MSDN.