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监听是否有网络事件发生。有就处理,没有就一直监听
首先需要用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监听是否有网络事件发生。有就处理,没有就一直监听
相关文章推荐
- Winsock I/O模型--WSAEventSelect模型
- socket的overlapped(重叠) I\O和其他socket I\O模型的区别
- WSAEventSelectI/O模型中FD_WRITE事件的处理以及如何发送数据
- WsaEventSelect封装的类,多线程
- 套接字I/O模型
- 事件选择模型
- windows Socket编程之EventSelect网络模型
- java 两个字符串取交集
- mem management
- jsp里面相对路径显示不出来图的问题
- 有关SQLite相关的文章收集
- Windows下Python工具pip的安装
- Linux下面如何安装Django
- 每日一练——判断两序列是否为同一二叉搜索树序列
- String、StringBuffer、StringBuilder的比较
- 【分享】WeX5的正确打开方式(7)——数据组件详解
- CSS中!important的使用
- MySQL修改root密码的多种方法
- /bin/bash^M: bad interpreter: No such file or directory
- gulp入门2