Windows的异步通知I/O模型

Windows的异步通知I/O模型

2019526

10:51

   

同步和异步直接百度一下应该还算很容易理解吧,虽然我一开始看这个同步和异步的时候也是疑惑了一下,觉得名字起的好奇怪啊。但是现在来看的话,名字起的还是意外的形象呢?有点迷。

   

  • 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
  • 异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,"真实"地执行着。整个过程,不会阻碍调用者的工作。

   

来自 <https://www.cnblogs.com/anny0404/p/5691379.html>

   

要是让我来打一个比方的话,就是比如有一条cpu的时间线(单核cpu的时间线当然是只有一条的),然后其他的进程或者线程就是一条一条的虚线。他们再一起可以重合成为cpu的时间线,当然他们自己本身是断断续续的。然而我们如果将他们缩放看来的话,会不会发现虚线之间空白的部分会越来越小,直至消失不见。这就好像多出了几个和cpu的时间线完全平行的时间线了。(对于单核cpu)

   

例如我们一开始的read(recv),write(send)都是同步方式的IO函数。select也是同步函数。

   

理解和实现异步通知I/O模型

异步通知I/O模型的实现方法有2种:WSAEventSelect函数,WSAAsyncSelect函数

其中WSAAsyncSelect函数需要指定Windows句柄以获取发生的事件(UI相关内容)

   

告知I/O状态变化的操作就是"通知"。I/O的状态变化可以分为不同的情况:

  • 套接字的状态变化:套接字的I/O状态变化
  • 发生套接字相关事件:发生套接字IO相关事件

   

指定某一套接字为事件监视对象:

#include <winsock2.h>

int WSAEventSelect(SOCKET s, WSAEVENT hEventObject,

long lNetworkEvents);

->>成功时返回0,失败时返回SOCKET_ERROR

   

s:套接字句柄

hEventObject: 事件对象句柄

lNetworkEvents:希望监视的事件类型信息

   

该函数以异步方式工作。只要s套接字种发生了lNetworkEvents种所指定的事件之一。WSAEventsSelect的异步线程就将hEventObject句柄所指向的内核对象更改为signaled状态。所以这个函数也称为"连接事件对象和套接字的函数"。

   

第三个参数的事件类型信息,可以通过位或方式同时指定多个信息:

  • FD_READ:是否存在需要读取的数据
  • FD_WRITE:能否以非阻塞方式传输数据
  • FD_OOB:是否收到带外数据
  • FD_ACCEPT:是否有新的连接请求
  • FD_CLOSE:是否有断开连接的请求

   

WSAEventSelect函数每次只能传递一个套接字的监视信息。但是于select函数相比的是,通过WSAEventSelect函数传递的套接字信息以注册到操作系统,所以无需再次调用。

   

manual-reset模式事件对象的其他创建方法

使用WSACreateEvent函数直接创建manual-reset模式non-signaled状态的事件对象

#include <winsock2.h>

WSAEVENT WSACreateEvent(void);

->>成功是返回事件对象句柄,失败时返回WSA_INVALID_EVENT

   

WSAEVENT的定义如下:

#define WSAEVENT HANDLE

   

销毁WSACreateEvent函数创建的事件对象

#include <winsock2.h>

BOOL WSACloseEvent(WSAEVENT hEvent);

->> 成功时返回TRUE,失败时返回FALSE

   

虽然直接使用CloseHandle也可以

   

验证是否发生事件

虽然使用WaitForMultipleEvents也是可以验证socket是否发生事件

#include <winsock2.h>

DWORD WSAWaitForMultipleEvents(DWORD cEvents,

const WSAEVENT * lphEvents);

->>成功时返回发生事件的对象组的最小索引,失败返回WSA_INVALID_EVENT,等待超时返回WSA_WAIT_TIMEOUT

   

cEvents:需要验证的事件对象的个数

lphEvents:事件对象句柄数组地址

fWaitAll:是否等待全部,TRUE是,FALSE否

dwTimeout:指定超时事件,以毫秒为单位。WSA_INFINITE阻塞

fAlertable:传递TRUE进入alertable_wait(可警告等待状态)

返回值:返回值减去WSA_WAIT_EVENT_0为发生事件的对象组的最小索引(如果有多个的话)

   

WSA_MAXIMUM_WAIT_EVENTS指定了WSAWaitForMultipleEvents函数同时可以监视的最大事件对象数。为64

   

posInfo = WSAWaitForMultipleEvents(numOfSock, hEventArray, FALSE, WSA_INFINITE, FALSE);

startIdx = posInfo - WSA_WAIT_EVENT_0;

for(I = startIdx; I < numOfSock; i++)

{

int sigEventIdx = WSAWaitForMultipleEvents(1, &hEventArray[i], TRUE, 0, FALSE);

}

   

区分事件类型

#include <winsock2.h>

int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject,

LPWSANETWORKEVENTS lpNetworkEvents);

->>成功时返回0,失败时返回SOCKET_ERROR

   

lpNetworkEvents:保存发生的事件类型信息和错误信息的WSANETWORKEVENTS结构体变量地址值

调用该函数以后才会将manual-reset模式的事件对象更改为non-signaled状态,所以之后不必再单独调用ResetEvent函数。

   

typedef struct _WSANETWORKEVENTS

{

long lNetworkEvents;

int iErrorCode[FD_MAX_EVENTS];

}WSANETWORKEVENTS, * LPWSANETWORKEVENTS;

lNetworkEvents保存发生的事件信息,使用位与&运算验证

iErrorCode数组种保存了对应事件的错误信息,使用iErrorCode[FD_XXX_BIT]查看对应事件的错误信息

   

WSANETWORKEVENTS netEvents;

WSAEnumNetworkEvents(hSock, hEvent, &netEvents);

if(netEvents.lNetworkEvents & FD_ACCEPT)

{

// FD_ACCEPT 事件处理

}

if(netEvents.lNetworkEvents & FD_READ)

{

// FD_READ 事件处理

}

if(netEvents.lNetworkEvents & FD_CLOSE)

{

// FD_CLOSE 事件处理

}

   

同时如果对应事件发生错误,那么iErrorCode[FD_XXX_BIT]中应存储0以外的值

WSANETWORKEVENTS netEvents;

WSAEnumNetworkEvents(hSock, hEvent, &netEvents);

if(netEvents.iErrorCode[FD_READ_BIT] != 0)

{

// 发生FD_READ事件相关错误

}

猜你喜欢

转载自www.cnblogs.com/freesfu/p/10925690.html