zoukankan      html  css  js  c++  java
  • 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事件相关错误

    }

  • 相关阅读:
    自定义中间件
    ASP.NET Core后台任务
    Hosted Services+Quartz实现定时任务调度
    .NET Core 中的路径问题
    js Worker 线程
    postMessage解决跨域跨窗口消息传递
    CentOS搭建KMS服务器
    CentOS安装最新Git
    Linux访问https报错
    EntityFramework Core几个基本命令的使用
  • 原文地址:https://www.cnblogs.com/freesfu/p/10925690.html
Copyright © 2011-2022 走看看