首页 > 代码库 > WsaEventSelect编程模型

WsaEventSelect编程模型

WsaEventSelect模型是一个不用主动去轮询所有客户端套接字是否有数据到来的模型,它也是在客户端有数据到来时,系统发送通知给我们的程序,但是,它不是发送消息,而是通过事件的方式来通知我们的程序,这就解决了WsaAsyncSelect模型只能用在windows程序的问题。

该模型的实现,我们也可以开辟两个线程来进行处理,一个用来接收客户端的连接请求,一个用来与客户端进行通信,用到的主要函数有WSAEventSelect,WSAWaitForMultipleEvents,WSAEnumNetworkEvents实现方式如下:

首先定义三个全局数组

SOCKET      g_SockArray[MAX_NUM_SOCKET];//存放客户端套接字

WSAEVENT    g_EventArray[MAX_NUM_SOCKET];//存放该客户端有数据到来时,触发的事件

UINT32      g_totalEvent = 0;//记录客户端的连接数

线程1处理函数如下:

  SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(7788);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));
    if ( nRet == SOCKET_ERROR )
    {
        DWORD errCode = GetLastError();
        return;
    }

    listen( listenSock, 5);

    sockaddr_in clientAddr;
    int nameLen = sizeof( clientAddr );
    while( g_totalEvent < MAX_NUM_SOCKET )
    {
        SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );
        if ( clientSock == INVALID_SOCKET )
        {
            continue;
        }
        g_SockArray[g_totalEvent] = clientSock;

        if( (g_EventArray[g_totalEvent] = WSACreateEvent()) == WSA_INVALID_EVENT )
        {
            continue;
        }

        WSAEventSelect( clientSock, g_EventArray[g_totalEvent],FD_READ | FD_CLOSE );
        g_totalEvent++;
    }
 线程2的处理函数如下:

 int nIndex = 0;
    char* recvBuffer =(char*)malloc( sizeof(char) * 1024 );

    if ( recvBuffer == NULL )
    {
    return;
    }

    memset( recvBuffer, 0, sizeof(char) * 1024 );

    while( true )
    {
        nIndex = WSAWaitForMultipleEvents( g_totalEvent, g_EventArray, FALSE, WSA_INFINITE,FALSE );
        if ( nIndex == WSA_WAIT_FAILED )
        {
            continue;
        }
        else
        { 
            WSAResetEvent( g_EventArray[ nIndex - WSA_WAIT_EVENT_0]);
            SOCKET clientSock = g_SockArray[ nIndex - WSA_WAIT_EVENT_0 ];
            WSANETWORKEVENTS wsaNetWorkEvent;

            int nRet = WSAEnumNetworkEvents( clientSock, g_EventArray[nIndex - WSA_WAIT_EVENT_0], &wsaNetWorkEvent );
            if ( SOCKET_ERROR == nRet )
            {
                continue;
            }
            else if ( wsaNetWorkEvent.lNetworkEvents & FD_READ )
            {
                if ( wsaNetWorkEvent.iErrorCode[FD_READ_BIT] != 0 )
                {
                    //occur error
                    closesocket( clientSock );
                }
                else 
                {
                    memset( recvBuffer, 0, sizeof(char) * 1024 );
                    nRet = recv( clientSock, recvBuffer, 1024, 0);
                    if ( nRet == SOCKET_ERROR )
                    {
                        closesocket( clientSock );
                    }
                    else
                    {
                        //todo:对接收到的客户端数据进行处理
                        }
                 }
             }
             else if( wsaNetWorkEvent.lNetworkEvents & FD_CLOSE )
             {
                if ( wsaNetWorkEvent.iErrorCode[FD_CLOSE_BIT] != 0 )
                {
                    //occur error
                    closesocket( clientSock );
                }
                else
                {
                    closesocket( clientSock );
                }  
             }
        }
    }

    if ( recvBuffer != NULL )
    {
        free( recvBuffer );
    }

该模型通过一个死循环里面调用WSAWaitForMultipleEvents函数来等待客户端套接字对应的Event的到来,一旦事件通知到达,就通过该套接字去接收数据。虽然WsaEventSelect模型的实现较前两种方法复杂,但它在效率和兼容性方面是最好的。

    select,wsaasyncselect,wsaeventselect三种模型虽然在效率方面有了不少的提升,但它们都存在一个问题,就是都预设了只能接收64个客户端连接,虽然我们在实现时可以不受这个限制,但是那样,它们所带来的效率提升又将打折扣,那又有没有什么模型可以解决这个问题呢?重叠I/0模型将解决这个问题


WsaEventSelect编程模型