WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似。该模型同样是接收 FD_XXX 之类的网络事件,但是是通过事件对象句柄通知,而非像 WSAAsyncSelect一样依靠Windows的消息驱动机制。? ? ?
? ? ? 与WSAAsyncSelect模型相同,WSAEventSelect将所有的SOCKET事件分为如下类型:(共十种)
? ? ? ? ? ? ? ? FD_READ , FD_WRITE , FD_OOB , FD_ACCEPT, FD_CONNECT , FD_CLOSE,
? ? ? ? ? ? ? ? FD_QOS , FD_GROUP_QOS , FD_ROUTING_INTERFACE_CHANGE , FD_ADDRESS_LIST_CHANGE
? ? ? 还有一个 FD_ALL_EVENTS? 代表所有的事件
? ? ? 其中 FD_READ 的定义如下:
?#define FD_READ_BIT? ? ? 0
?#define FD_READ? ? ? ? ? (1 << FD_READ_BIT)? // = 1
? ? ? 其他的定义也都类似,比如: FD_ACCEPT_BIT = 3
但是并不是每一种SOCKET都能发生所有的事件,比如监听SOCKET只能发生 FD_ACCEPT 和 FD_CLOSE 事件。
在WSAEventSelect模型中,基本流程如下:
?1. 创建一个事件对象数组,用于存放所有的事件对象;
?2. 创建一个事件对象(WSACreateEvent);
?3. 将一组你感兴趣的SOCKET事件与事件对象关联(WSAEventSelect),然后加入事件对象数组;
?4. 等待事件对象数组上发生一个你感兴趣的网络事件(WSAWaitForMultipleEvents);
?5. 对发生事件的事件对象查询具体发生的事件类型(WSAEnumNetworkEvents);
?6. 针对不同的事件类型进行不同的处理;
?7. 循环进行 .4
? ? ? ? 对于TCP服务端程序而言,在创建一个监听SOCKET,绑定至某个端口然后监听后,可以创建一个事件对象然后与 FD_ACCEPT 和 FD_CLOSE 事件
关联。在第6步时对于 FD_ACCEPT 事件可以将accept得到的SOCKET关联 FD_WRITE,FD_READ,FD_CLOSE事件后加入事件对象数组。
?
WSAEVENT WSACreateEvent(void);
? ? ? 创建一个 事件对象,实际上 WSAEVENT就是一个 HANDLE
?
?
int WSAEventSelect(
? _In_? SOCKET s,? ? ? ? ? ? // 需要关联的SOCKET
? _In_? WSAEVENT hEventObject,? ? // 需要关联的事件对象
? _In_? long lNetworkEvents? ? // 感兴趣的网络事件,不同的事件可以用 | 合并, FD_ALL_EVENTS 代表所有的事件
);
? ? ? 函数执行成功将返回 0 ,否则返回 SOCKET_ERROR, 可调用WSAGetLastError() 查看具体的错误代码
?
?
DWORD WSAWaitForMultipleEvents(
? _In_? DWORD cEvents,? ? // 事件对象数组的数量
? _In_? const WSAEVENT *lphEvents,? // 事件对象数组
? _In_? BOOL fWaitAll,? ? // 是否等待所有的事件对象受信,显然一般情况下是false
? _In_? DWORD dwTimeout,? // 超时时限,单位是毫秒,WSA_INFINITE 为无穷大
? _In_? BOOL fAlertable? // 该模型下忽略,应该设置为false
);
?
? ? ? 如果执行失败返回 WSA_WAIT_IO_COMPLETION ; 如果是超时,则返回 WSA_WAIT_TIMEOUT
? ? ? 如果 函数执行成功将会返回一个值,分布在 区间 [ WSA_WAIT_EVENT_0 ,(WSA_WAIT_EVENT_0+cEvents-1) ] 内
? ? ? 也就是说返回值 nRet-WSA_WAIT_EVENT_0 将是发生事件的对象在事件对象数组中的下标。
?
int WSAEnumNetworkEvents(
? _In_? SOCKET s,? ? //? ? 发生事件的SOCKET
? _In_? WSAEVENT hEventObject,? //? ? 发生事件的事件对象
? _Out_? LPWSANETWORKEVENTS lpNetworkEvents //? ? 发生的网络事件
);
? ? ? 如果该函数执行成功将会返回0,然后可以通过查询网络事件判断到底发生了什么事件。
?
? ? ? WSANETWORKEVENTS的定义如下:
typedef struct _WSANETWORKEVENTS {
? ? ? long lNetworkEvents;? // 发生的网络事件类型
? ? ? int iErrorCode[FD_MAX_EVENTS]; // 网络事件对应的错误代码
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
? ? ? 比如当发生 FD_READ 事件时, 那么 networkEvent.lNetworkEvents&FD_READ 将为真,同时 networkEvent.iErrorCode[FD_READ_BIT]
标明了此时的错误代码。
代码示例:(你还需要一个 client程序,自己写或者找吧)
#include
#include
#pragma comment(lib,"ws2_32.lib")
using std::cout;
using std::cin;
using std::endl;
using std::ends;
void WSAEventServerSocket()
{
? ? SOCKET server = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
? ? if(server == INVALID_SOCKET){
? ? ? ? cout<<"创建SOCKET失败!,错误代码:"<? ? ? ? return ;
? ? }
? ? int error = 0;
? ? sockaddr_in addr_in;
? ? addr_in.sin_family = AF_INET;
? ? addr_in.sin_port = htons(15000);
? ? addr_in.sin_addr.s_addr = INADDR_ANY;
? ? error= ::bind(server,(sockaddr*)&addr_in,sizeof(sockaddr_in));
? ? if(error == SOCKET_ERROR){
? ? ? ? cout<<"绑定端口失败!,错误代码:"<? ? ? ? return ;
? ? }
? ? listen(server,5);
? ? if(error == SOCKET_ERROR){
? ? ? ? cout<<"监听失败!,错误代码:"<? ? ? ? return ;
? ? }
? ? cout<<"成功监听端口 :"<
? ? WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];? ? ? ? // 事件对象数组
? ? SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];? ? ? ? ? ? // 事件对象数组对应的SOCKET句柄
? ? int nEvent = 0;? ? ? ? ? ? ? ? ? ? // 事件对象数组的数量
? ? WSAEVENT event0 = ::WSACreateEvent();
? ? ::WSAEventSelect(server,event0,FD_ACCEPT|FD_CLOSE);
? ? eventArray[nEvent]=event0;
? ? soc