WinSock I/O 模型 -- WSAEventSelect 模型

简介

WSAEventSelect 模型也是 WinSock 中最常见的异步 I/O 模型。

这篇文章我们就来看看如何使用 WSAEventSelect api 来实现一个简单的 TCP 服务器.

API 基础

WSAEventSelect

WSAEventSelect 用来把一个 SOCKET 对象和一个 WSAEVENT 对象关联起来。 lNetworkEvents 表示我们关心的 FD_XXX 网络事件. 如果关心多个 SOCKET 事件,可以使用 OR 的方式指定多个 FD_XXX 标志。

int WSAAPI WSAEventSelect(
  SOCKET   s,
  WSAEVENT hEventObject,
  long     lNetworkEvents
);

当特定的事件发生在相应的 SOCKET 上,该 SOCKET 上接下来的事件将会被阻塞,直到当前的事件被应用处理. 该事件被处理之后,接下来的事件将可以被进一步触发.

事件 处理函数
FD_READ recv, recvFrom, WSARecv, WSARecvEx, WSARecvFrom
FD_WRITE send, sendTo, WSASend, WSASentTo
FD_OOB recv, recvFrom, WSARecv, WSARecvEx, WSARecvFrom
FD_ACCEPT accept, AcceptEx, WSAAccept
FD_CONNECT None
FD_CLOSE None
FD_QOS WSAIoctl (with SIO_GET_QOS)
FD_GROUP_QOS Reserved
FD_ROUTINE_INTERFACE_CHANGE WSAIoctl (with SIO_ROUTINE_INTERFACE_CHANGE)
FD_ADDRESS_LIST_CHANGE WSAIoctl (with SIO_ADDRESS_LIST_CHANGE)
WSAEvent

WSACreateEvent 方法用来创建一个 WSAEvent 对象

WSAEVENT WSAAPI WSACreateEvent();

WSAWaitForMultipleEvents 用于等待一组事件中的一个或全部被触发。

DWORD WSAAPI WSAWaitForMultipleEvents(
  DWORD          cEvents,
  const WSAEVENT *lphEvents,
  BOOL           fWaitAll,
  DWORD          dwTimeout,
  BOOL           fAlertable
);
  • cEvents:指定 lphEvents 数组中事件对象的数量。 该参数的最大值是 WSA_MAXIMUM_WAIT_EVENTS (64)
  • lphEvents:事件对象的集合
  • fWaitAll: 指定等待 lphEvents 中所有事件被触发或者其中之一被触发。 如果指定为 TRUE, 那么该函数只有在所有事件对象都被触发之后才会返回。 如果指定为 FALSE, 当事件集合中任何一个事件被触发之后,该方法就会返回。如果在这种情况下有多个事件对象被触发,那个返回值将会返回该事件集合中索引值最小的索引值. 索引值减去 WSA_WAIT_EVENT_0 便是指向 lphEvents 中被触发的事件的索引值.
  • dwTimeout: 如果在 timeout 事件间隔内,没有事件被触发,函数不会一直阻塞,而是在等待 timeout 毫秒后返回。 指定该参数为 WSA_INFINITE, 该函数会一直等待,直到有事件被触发. 指定该参数为 0, 该函数会立即返回.
  • fAlertable: 略

WSAEnumNetworkEvents 用于查询当前 SOCKET 上触发事件对象 hEventObject 的对应 socket 事件(FD_READ, FD_WRITE 等).

int WSAAPI WSAEnumNetworkEvents(
  SOCKET             s,
  WSAEVENT           hEventObject,
  LPWSANETWORKEVENTS lpNetworkEvents
);

实现思路

  1. 创建一个 socket 作为监听 socket
  2. 使用 WSAEventSelect 监听该 SOCKET 上的网络事件
  3. 使用 WSAWaitForMultipleEvents 等待 SOCKET 事件
  4. 当 SOCKET 上有事件被触发,使用 WSAEnumNetworkEvents 查询具体的 SOCKET 事件,并使用相应的 API 处理事件.
  5. 当有新的 SOCKET 连接到来,接收该连接,重复 2-4 步骤.

实例

接下来我们通过一个实例来看看如何实现.

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib,"ws2_32.lib")

#define PORT 8080
#define DATA_BUFSIZE 8192
 
typedef struct _SOCKET_CONTEXT {
   
   CHAR   Buffer[DATA_BUFSIZE];
   WSABUF DataBuf;
   SOCKET Socket;
   DWORD  BytesSEND;
   DWORD  BytesRECV;
} SOCKET_CONTEXT, * LPSOCKET_CONTEXT;

BOOL CreateSocketInformation(SOCKET s);
void FreeSocketInformation(DWORD Event);

// 这里我们维护了如下数据结构:
//     EeventArray: 我们为每个 SOCKET 对象创建一个对应的事件对象,以便我们能监听该 SOCKET 上的网络事件
//     SocketArray: 毫无疑问,我们也需要维护所有SOCKET连接的的数组。其中包含 Listen Socket

DWORD            EventTotal = 0;
WSAEVENT         EventArray[WSA_MAXIMUM_WAIT_EVENTS];
LPSOCKET_CONTEXT SocketArray[WSA_MAXIMUM_WAIT_EVENTS];

int main() {
   

  SOCKET           ListenSocket;
  SOCKET           AcceptSocket;
  SOCKADDR_IN      Addr;
  LPSOCKET_CONTEXT SocketContext;
  WSANETWORKEVENTS NetworkEvents;
  DWORD            Event;
  WSADATA          wsaData;
  DWORD            Flags;
  DWORD            RecvBytes;
  DWORD            SendBytes;

  // 初始化 Listen Socket 对象
  if (WSAStartup(0x0202, &wsaData) != 0) {
   
    printf("WSAStartup() failed with error %d\n", WSAGetLastError());
    return 1;
  }

  if ((ListenSocket = socket(AF_INET
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值