windows套接字I/0模型-WSAEventSelect 模型
2023-5-10 14:51:22 Author: 安全狗的自我修养(查看原文) 阅读量:16 收藏

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

 


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247487949&idx=1&sn=2b4667f200af1c152bc84d1f821c6dc5&chksm=c13f2284f648ab9225ac8c8bf6a987462af29ee8a1510c4a06ce63a6e30a50f9f98202c76638#rd
如有侵权请联系:admin#unsafe.sh