您的位置:首页 > 其它

WSAEventSelect(事件选择)模型

2016-07-12 14:04 666 查看
http://joychou.org/index.php/Misc/WSAEventSelect.html

首先需要用WSACreateEvent创建一个事件对象,其初始化状态为状态为“未受信” 并且人工重置。

函数原型

WSAEVENT WSACreateEvent(void);

然后利用WSAEventSelect绑定“监听”套接字到刚创建的事件对象中,并设置感兴趣的网络事件。

int WSAEventSelect(

  __in  SOCKET s,    //套接字

  __in  WSAEVENT hEventObject,    //事件对象

  __in  long lNetworkEvents    // 网络事件

);

再利用WSAWaitForMultipleEvents等待事件对象为“受信”状态。

问:怎么才是成为“受信”状态?

当事件对象绑定的套接字进行了I/O处理,

比如客户端进行了连接或者客户端发送了数据,再或者客户端关闭套接字,

事件对象都会变成“受信”状态。

DWORD WSAWaitForMultipleEvents(

  __in  DWORD cEvents,    // 事件对象个数

  __in  const WSAEVENT* lphEvents,     // 事件对象数组

  __in  BOOL fWaitAll,    // 是否全部等待 

  __in  DWORD dwTimeout,    // 等待时间

  __in  BOOL fAlertable    // 重叠I/O中使用,这里设置为FALSE

);

最后用WSAEnumNetworkEvents检查发生了什么网络事件,最后一个参数返回发生了什么网络事件,

这个API如果第二个参数如果不设置为NULL,那么api调用完后,事件对象会变成“未受信”状态。

如果设置为NULL,就必须用WSAResetEvent设置为“未受信”。

int WSAEnumNetworkEvents(

  __in   SOCKET s,    // 套接字,不过必须使用数组

  __in   WSAEVENT hEventObject,    // 事件对象  不过必须使用数组

  __out  LPWSANETWORKEVENTS lpNetworkEvents    // 网络事件结构体指针

);

WSANETWORKEVENTS结构体

typedef struct _WSANETWORKEVENTS

 { 

     long lNetworkEvents;  

     int iErrorCode[FD_MAX_EVENTS];

} WSANETWORKEVENTS,  *LPWSANETWORKEVENTS;

最后的主要代码:

// 创建一个事件对象,状态为“未受信”  并且人工重置

    WSAEVENT m_listenEvent = WSACreateEvent();

    // 绑定 “监听”套接字到事件对象中,并设置感兴趣的网络事件

    WSAEventSelect(pThis->m_SockListen, m_listenEvent, FD_ACCEPT | FD_CLOSE);

    DWORD dwEventTotal = 0, dwIndex = 0, dwRet = 0;

    SOCKET ArrSocket[WSA_MAXIMUM_WAIT_EVENTS] = {0};

    WSAEVENT ArrEvent[WSA_MAXIMUM_WAIT_EVENTS] = {0};

    WSANETWORKEVENTS m_NetWorkEvents = {0};

    TCHAR szBuf[1024] = {0};

    ArrSocket[dwEventTotal] = pThis->m_SockListen;

    ArrEvent[dwEventTotal] = m_listenEvent;

    ++dwEventTotal;

    while(TRUE)

    {

        

        // 等待事件对象为受信,不受信的话会返回超时

        // 当事件对象绑定的套接字进行了I/O处理,事件对象会变成“受信”状态

        dwRet = WSAWaitForMultipleEvents(dwEventTotal, ArrEvent, FALSE, 100, FALSE);

        // 0x102 WSA_WAIT_TIMEOUT

        if (dwRet == WSA_WAIT_TIMEOUT || dwRet == WSA_WAIT_FAILED)  

        {

            continue; //如果返回值是错误或是超时,那么继循环

        }

        // 获取索引

        dwIndex = dwRet - WSA_WAIT_EVENT_0;

        // 检查是发生了什么网络事件

        // 第二个参数传入事件对象句柄,将其设为“未受信”

        WSAEnumNetworkEvents(ArrSocket[dwIndex], ArrEvent[dwIndex], &m_NetWorkEvents);

        if (m_NetWorkEvents.lNetworkEvents == FD_ACCEPT)

        {

            // 如果发生错误

            if (m_NetWorkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)

            {

                continue;

            }

            // 接收客户端的连接

            // 如果第二个填入一个sockaddr_in结构体,可以获取客户端的ip

            pThis->m_SockClient = accept(ArrSocket[dwIndex], NULL,NULL);

            if (pThis->m_SockClient == INVALID_SOCKET)

            {

                continue;

            }

            // 创建一个新的事件对象用来绑定和客户端连接的套接字

            WSAEVENT NewEvent = WSACreateEvent();

            // 绑定 “连接”套接字 到NewEvent事件对象中,监听FD_READ和FD_CLOSE

            WSAEventSelect(pThis->m_SockClient, NewEvent, FD_READ | FD_CLOSE);

            

            ArrSocket[dwEventTotal] = pThis->m_SockClient;

            ArrEvent[dwEventTotal] = NewEvent;

            ++dwEventTotal;

            ++pThis->m_ClientNum;

        }

        if (m_NetWorkEvents.lNetworkEvents == FD_READ)

        {

            if (m_NetWorkEvents.iErrorCode[FD_READ_BIT] != 0)

            {

                continue;

            }

            recv(ArrSocket[dwIndex], (char *)szBuf, 1024, 0);

            pThis->ShowMsg(szBuf);

        }

        // 当连接被关闭时

        if (m_NetWorkEvents.lNetworkEvents & FD_CLOSE)

        {

                if (m_NetWorkEvents.iErrorCode[FD_CLOSE_BIT] != 0)

                {

                    continue;

                }

                pThis->ShowMsg(_T("客户端已退出"));   

                closesocket(ArrSocket[dwIndex]);

                WSACloseEvent(ArrEvent[dwIndex]);

                //将此套节字和事件对象从数组中清除。  

                for (UINT i = dwIndex; i < dwEventTotal-1; i++)

                {

                    ArrSocket[i] = ArrSocket[i+1];  //[i+1]为0

                    ArrEvent[i] = ArrEvent[i+1];    //[i+1]为0   

                }

                dwEventTotal--;         

        }

    }

注意:

在FD_CLOSE这里,我总是遇到一个问题,m_NetWorkEvents.iErrorCode[FD_CLOSE_BIT]总是不等于0,即发生了错误,刚开始不知道为什么,一直以为是服务端的问题。不过发生的错误码是0x2745,网上搜索下错误码的意思是

[10053(也就是0x2745的十进制)] 您的主机中的软件放弃了一个已建立的连接。

说明已经建立的连接在无意间关闭了,而且服务端不知道。

我们需要做的,只是在客户端点击x的时候(MFC中重载对话框类的OnCancle函数),

关闭掉套接字即可。

特点:

多个客户端连接

利用WSAWaitForMultipleEvents监听是否有网络事件发生。有就处理,没有就一直监听
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息