1.简介
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.案例
#include <stdio.h>
#include <iostream.h>
#include <windows.h>
// 初始化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] = '\0';
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;
}
公众号: 安全狗的自我修养
抖音: haidragon
bibi: haidragonx