WSAEventSelect(事件选择)模型

来源:互联网 发布:红包尾数控制软件 编辑:程序博客网 时间:2024/05/17 07:15

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监听是否有网络事件发生。有就处理,没有就一直监听

0 0
原创粉丝点击