windows套接字I/0模型-WSAEventSelect 模型

admin 2023年5月12日09:41:08评论36 views字数 3797阅读12分39秒阅读模式
1.简介

WSAEventSelect 模型是 Windows 网络编程中的一种异步 I/O 模型,可以通过事件对象实现异步操作和事件通知。与 WSAAsyncSelect 模型相比,WSAEventSelect 模型可以同时监听多种网络事件,例如同一套接字的可读和可写事件可以同时监听,也可以同时监听多个套接字的可读事件,因此在大型程序和复杂的网络应用中更加灵活和可扩展。

windows套接字I/0模型-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] = '&#x0;';              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 模型

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月12日09:41:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   windows套接字I/0模型-WSAEventSelect 模型http://cn-sec.com/archives/1724241.html

发表评论

匿名网友 填写信息