WSAEventSelect 模型是 Windows 网络编程中的一种异步 I/O 模型,可以通过事件对象实现异步操作和事件通知。与 WSAAsyncSelect 模型相比,WSAEventSelect 模型可以同时监听多种网络事件,例如同一套接字的可读和可写事件可以同时监听,也可以同时监听多个套接字的可读事件,因此在大型程序和复杂的网络应用中更加灵活和可扩展。
使用方法
(1)创建套接字
首先需要创建一个套接字,可以使用 socket 函数创建一个 TCP 或 UDP 套接字:
SOCKET
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
(2)创建事件对象
然后需要创建一个事件对象,可以使用WSACreateEvent
函数创建一个事件对象:
WSAEVENT
event = ::WSACreateEvent();
(3)使用 WSAEventSelect 函数
然后使用 WSAEventSelect 函数将套接字与事件对象关联,指定需要监听的网络事件:
WSAEventSelect(sock, hEvent, FD_READ
| FD_WRITE |
FD_CLOSE
| FD_CONNECT);
(4)等待网络事件
接下来使用 WaitForSingleObject 或 WaitForMultipleObjects 函数等待事件对象的信号:
DWORD dwResult = WaitForSingleObject(hEvent, INFINITE);
if
(dwResult == WAIT_OBJECT_0) {
// 处理网络事件
}
当有网络事件发生时,事件对象会被信号,WaitForSingleObject 函数会返回 WAIT_OBJECT_0,此时可以调用 WSAGetOverlappedResult 函数获取异步操作的结果,或者直接处理网络事件。
(5)实现回调函数
可以在窗口消息循环中使用 WSAAsyncSelect 函数实现回调函数,也可以使用 WSAWaitForMultipleEvents 函数实现回调函数,具体可以参考 Microsoft 官方文档和示例代码。
3.注意事项
在使用 WSAEventSelect 模型时,需要注意以下几点:
(1)事件对象需要在异步操作完成之前一直保持有效,可以使用 WSACloseEvent 函数关闭事件对象。
(2)需要使用 WSAGetOverlappedResult 函数获取异步操作的结果,可以将套接字和事件对象关联的 WSAOVERLAPPED 结构体作为参数传递。
(3)可以使用 WSAResetEvent 函数重置事件对象,以便重复使用。
优缺点
WSAEventSelect 模型的优点是能够同时监听多种网络事件,适用于大型程序和复杂的网络应用,也可以与 Windows 事件通知机制结合使用,具有更高的灵活性和可扩展性。缺点是相对于 WSAAsyncSelect 模型更为复杂,需要在代码中实现事件循环和状态机,也需要更多的资源,例如事件对象和 WSAOVERLAPPED 结构体等。此外,WSAEventSelect 模型也是 Windows 平台特定的异步 I/O 模型,不适用于跨平台的网络应用。
5.总结
WSAEventSelect 模型是 Windows 网络编程中的一种异步 I/O 模型,可以通过事件对象实现异步操作和事件通知。与 WSAAsyncSelect 模型相比,WSAEventSelect 模型可以同时监听多种网络事件,更加灵活和可扩展。使用 WSAEventSelect 模型需要创建套接字、事件对象,使用 WSAEventSelect 函数关联套接字和事件对象,等待网络事件并处理,以及实现回调函数等步骤。需要注意事件对象需要在异步操作完成之前一直保持有效,需要使用 WSAGetOverlappedResult 函数获取异步操作的结果,可以使用 WSAResetEvent 函数重置事件对象,也需要在代码中实现事件循环和状态机等逻辑。WSAEventSelect 模型适用于大型程序和复杂的网络应用,具有更高的灵活性和可扩展性。
6.案例
// 初始化Winsock库
CInitSock theSock;
int
main
()
{
// 事件句柄和套节字句柄表
WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];
int
nEventTotal =
0
;
USHORT nPort =
4567
;
// 此服务器监听的端口号
// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in
sin
;
sin
.sin_family = AF_INET;
sin
.sin_port = htons(nPort);
sin
.sin_addr.S_un.S_addr = INADDR_ANY;
if
(::bind(sListen, (sockaddr*)&
sin
,
sizeof
(
sin
)) == SOCKET_ERROR)
{
printf
(
" Failed bind() n"
);
return
-1
;
}
::listen(sListen,
5
);
// 创建事件对象,并关联到新的套节字
WSAEVENT event = ::WSACreateEvent();
::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);
// 添加到表中
eventArray[nEventTotal] = event;
sockArray[nEventTotal] = sListen;
nEventTotal++;
// 处理网络事件
while
(TRUE)
{
// 在所有事件对象上等待
int
nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
// 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
nIndex = nIndex - WSA_WAIT_EVENT_0;
for
(
int
i=nIndex; i<nEventTotal; i++)
{
nIndex = ::WSAWaitForMultipleEvents(
1
, &eventArray[i], TRUE,
1000
, FALSE);
if
(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
{
continue
;
}
else
{
// 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
WSANETWORKEVENTS event;
::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);
if
(event.lNetworkEvents & FD_ACCEPT)
// 处理FD_ACCEPT通知消息
{
if
(event.iErrorCode[FD_ACCEPT_BIT] ==
0
)
{
if
(nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
{
printf
(
" Too many connections! n"
);
continue
;
}
SOCKET sNew = ::accept(sockArray[i],
NULL
,
NULL
);
WSAEVENT event = ::WSACreateEvent();
::WSAEventSelect(sNew, event, FD_READ|FD_CLOSE|FD_WRITE);
// 添加到表中
eventArray[nEventTotal] = event;
sockArray[nEventTotal] = sNew;
nEventTotal++;
}
}
else
if
(event.lNetworkEvents & FD_READ)
// 处理FD_READ通知消息
{
if
(event.iErrorCode[FD_READ_BIT] ==
0
)
{
char
szText[
256
];
int
nRecv = ::recv(sockArray[i], szText,
strlen
(szText),
0
);
if
(nRecv >
0
)
{
szText[nRecv] =
'�'
;
printf
(
"接收到数据:%s n"
, szText);
}
}
}
else
if
(event.lNetworkEvents & FD_CLOSE)
// 处理FD_CLOSE通知消息
{
if
(event.iErrorCode[FD_CLOSE_BIT] ==
0
)
{
::closesocket(sockArray[i]);
for
(
int
j=i; j<nEventTotal
-1
; j++)
{
sockArray[j] = sockArray[j+
1
];
sockArray[j] = sockArray[j+
1
];
}
nEventTotal--;
}
}
else
if
(event.lNetworkEvents & FD_WRITE)
// 处理FD_WRITE通知消息
{
}
}
}
}
return
0
;
}
原文始发于微信公众号(安全狗的自我修养):windows套接字I/0模型-WSAEventSelect 模型
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论