WSAEventSelect

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 <Windows.h>
#include <iostream>
#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失败!,错误代码:"<<WSAGetLastError()<<endl;
        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<<"绑定端口失败!,错误代码:"<<WSAGetLastError()<<endl;
        return ;
    }

listen(server,5);
    if(error == SOCKET_ERROR){
        cout<<"监听失败!,错误代码:"<<WSAGetLastError()<<endl;
        return ;
    }
    cout<<"成功监听端口 :"<<ntohs(addr_in.sin_port)<<endl;

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;
    sockArray[nEvent]=server;
    nEvent++;

while(true){
        int nIndex = ::WSAWaitForMultipleEvents(nEvent,eventArray,false,WSA_INFINITE,false);
        if( nIndex == WSA_WAIT_IO_COMPLETION || nIndex == WSA_WAIT_TIMEOUT ){
            cout<<"等待时发生错误!错误代码:"<<WSAGetLastError()<<endl;
            break;
        }
        nIndex = nIndex - WSA_WAIT_EVENT_0;
        WSANETWORKEVENTS event;
        SOCKET sock = sockArray[nIndex];
        ::WSAEnumNetworkEvents(sock,eventArray[nIndex],&event);
        if(event.lNetworkEvents & FD_ACCEPT){
            if(event.iErrorCode[FD_ACCEPT_BIT]==0){
                if(nEvent >= WSA_MAXIMUM_WAIT_EVENTS){
                    cout<<"事件对象太多,拒绝连接"<<endl;
                    continue;
                }
                sockaddr_in addr;
                int len = sizeof(sockaddr_in);
                SOCKET client = ::accept(sock,(sockaddr*)&addr,&len);
                if(client!= INVALID_SOCKET){
                    cout<<"接受了一个客户端连接 "<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<endl;
                    WSAEVENT eventNew = ::WSACreateEvent();
                    ::WSAEventSelect(client,eventNew,FD_READ|FD_CLOSE|FD_WRITE);
                    eventArray[nEvent]=eventNew;
                    sockArray[nEvent]=client;
                    nEvent++;
                }
            }
        }else if(event.lNetworkEvents & FD_READ){
            if(event.iErrorCode[FD_READ_BIT]==0){
                char buf[2500];
                ZeroMemory(buf,2500);
                int nRecv = ::recv( sock,buf,2500,0);
                if(nRecv>0){
                    cout<<"收到一个消息 :"<<buf<<endl;
                    char strSend[] = "I recvived your message.";
                    ::send(sock,strSend,strlen(strSend),0);
                }
            }
        }else if(event.lNetworkEvents & FD_CLOSE){
            ::WSACloseEvent(eventArray[nIndex]);
            ::closesocket(sockArray[nIndex]);
            cout<<"一个客户端连接已经断开了连接"<<endl;
            for(int j=nIndex;j<nEvent-1;j++){
                eventArray[j]=eventArray[j+1];
                sockArray[j]=sockArray[j+1];
            }
            nEvent--;
        } else if(event.lNetworkEvents & FD_WRITE ){
            cout<<"一个客户端连接允许写入数据"<<endl;
        }
    } // end while
    ::closesocket(server);
}

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    int error; 
    WORD wVersionRequested;    
    wVersionRequested = WINSOCK_VERSION; 
    error = WSAStartup( wVersionRequested , &wsaData );
    if ( error != 0 ) {
        WSACleanup();
        return 0;
    }

WSAEventServerSocket();

WSACleanup();
    return 0;
}

// 解释一下,为什么我在 socket函数前面加上 :: 
因为我前面写的时候本来用了thread库准备开一个线程运行Server,另一个运行Client。
结果 用了 using namespace std;  后,正好引入了bind函数(std的那个模板)把 socket的bind给覆盖了,
然后就一直是 错误了,查下错误代码是 10022(无效参数),检查时才发现的。

时间: 2024-10-16 11:13:31

WSAEventSelect的相关文章

套接字I/O模型-WSAEventSelect(转载)

和WSAAsyncSelect类似,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知. 该模型最主要的区别是在于网络事件是由对象句柄完成的,而不是通过窗口例程完成. 事件通知 事件通知模型要求应用程序针对打算使用的每一个套接字,首先创建一个事件对象.创建方法是调用WSACreateEvent函数: WSAEVENT WSACreateEvent(void); WSACreateEvent的返回值很简单,就是一个人工重设的事件对象句柄,一旦得到了事件对象句柄之后,必须将它与某个

winsock编程WSAEventSelect模型

winsock编程WSAEventSelect模型 WSAEventSelect模型和WSAAsyncSelec模型类似,都是用调用WSAXXXXXSelec函数将socket和事件关联并注册到系统,并将socket设置成非阻塞模式.二者不同之处在于socket事件的通知方法:WSAAsyncSelec模型利用窗口句柄和消息映射函数通知网络事件,而WSAEventSelect模型利用WSAEVENT通知网络事件.完成WSAEventSelect模型需要涉及以下函数或结构: 1:WSAEventS

WSAEventSelect IO复用模型

1 今天帮一学习WSAEventSelect的网友排查一个测试用服务器端recv返回0的问题,出现这个问题直观判断一般是客户端socket关闭了,可是他的代码很简单并且是本机测试,通过wireshark抓包也没有发现客户端发送了FIN分节,错误码为0,一切看起来都是正常的.正当无思路时,突然想到会不会是将recv的缓冲区长度参数给设置为0了呢,后发现果然如此,代码如下: else if(event.lNetworkEvents & FD_READ) // 处理FD_READ通知消息 { if(e

AcceptEx与WSAEventSelect和Accept

(转自论坛的一个帖子http://bbs.csdn.net/topics/280032853) AcceptEx主要用于向完成端口 投递一个或多个的连接请求..当有连接时进来,这里分两种情况: 1.Accept 不接受数据,工作线程收到IO完成操作的通知. 2.Aceept 必须接收到客户端的部分数量,工作线程才会收到IO完成操作的通知. 我觉的AcceptEx完全可以被WSAEventSelect取代,只要我们注册了FD_Accept通知,那么当有连接进来时,就会产生消息,那么我 们就可以在消

WsaEventSelect编程模型

WsaEventSelect模型是一个不用主动去轮询所有客户端套接字是否有数据到来的模型,它也是在客户端有数据到来时,系统发送通知给我们的程序,但是,它不是发送消息,而是通过事件的方式来通知我们的程序,这就解决了WsaAsyncSelect模型只能用在windows程序的问题. 该模型的实现,我们也可以开辟两个线程来进行处理,一个用来接收客户端的连接请求,一个用来与客户端进行通信,用到的主要函数有WSAEventSelect,WSAWaitForMultipleEvents,WSAEnumNet

WSAEventSelect模型编程 详解

转自:http://blog.csdn.net/wangjieest/article/details/7042108 WSAEventSelect模型编程 WSAEventSelect模型编程这个模型是一个简单的异步事件模型,使用起来比较方便,现在说一下其的具体的用法和需要注意的地方.一,模型的例程(服务端):先举一个王艳平网络通信上的例子: [cpp] view plaincopyprint? //////////////////////////////////////////////////

WSAEventSelect模型详解

WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似.       该模型同样是接收 FD_XXX 之类的网络事件,但是是通过事件对象句柄通知,而非像 WSAAsyncSelect一样依靠Windows的消息驱动机制. 与WSAAsyncSelect模型相同,WSAEventSelect将所有的SOCKET事件分为如下类型:(共十种)                FD_READ , FD_WRITE , FD_OOB

[00024]-[2015-09-19]-[02]-[WSAEventSelect 模型]

套接字WSAEventSelect模型WSAEventSelect模型和WSAAsyncSelect模型相似,他们的区别是:网络事件发生时系统通知应用程序的形式不同,WSAAsyncSelect模型以消息的形式通知应用程序,而WSAEventSelect模型以事件的形式通知应用程序: WSAAsyncSelect模型和WSAEventSelect模型 相同点:[一]这两个模型都是异步模型[二]当网络事件发生时,应用程序都可以接受到网络通知 [WSAEventSelect]函数 int WSAEv

套接字I/O模型之WSAEventSelect

转自 http://blog.csdn.net/wanjingwei/article/details/4306609 今天我又学习了一种新的套接字I/O模型------WSAEventSelect,他与WSAAsyncSelect一样也是一种异步事件通知模型,不同的是WSAAsyncSelect是与窗口句柄关联在一起的,必须要要窗口才行,而WSAEventSelect是与事件对象关联的.这个模型的基本思路是为感兴趣的一组网络事件创建一个事件对象,再调用WSAEventSelect函数将网络事件和