重叠I/O(Overlapped I/O)模型的学习

发布时间 2023-06-05 02:54:53作者: 苏显
  • 参考与学习:https://blog.csdn.net/PiggyXP/article/details/114883
     
  • 重叠结构(OVERLAPPED), Windows中所有的异步通信都是基于它的. 至于为什么叫Overlapped?Jeffrey Richter的解释是因为“执行I/O请求的时间与线程执行其他任务的时间是重叠(overlapped)的”
  • 优势:
    • 可以运行在支持Winsock2的所有Windows平台(而不像完成端口只是支持NT系统)。
    • 比起阻塞、select轮询、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。
      • 前面那些模型是数据到达并拷贝到套接字的缓冲区中,再通知应用程序到位了来拿吧,应用程序再把缓冲区的数据取走;Overlapped I/O模型下则是应用程序直接投递一个缓冲区,当数据到达套接字后将直接被拷贝到投递的缓冲区。
    • 《windows网络编程》中提供的试验结果证明了该模式的高性能。
  • 基本原理
    • 重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,就可以通过自己另外的代码来处理这些数据。
  • PS:使用Overlapped I/O,将会使用WSASend、WSARecv,WSASendto、WSARecvFrom代替原先的Send、Recv...进行传输。这些WSA~系列的函数都包含一个Overlapped参数。
  • 有两个方法可用来管理重叠IO请求的完成情况(即接到重叠操作完成的通知):
    • 事件对象通知(event object notification)
    • 完成例程(completion routines)
  • 事件通知实现Overlapped I/O
    • 基于事件通知,就要将Windows事件对象与WSAOVERLAPPED结构关联。由WSAOVERLAPPED结构可见:
      • WSAOVERLAPPED结构

        • 五参hEvent即用来绑定WSAEvent对象

    • 使用Overlapped I/O,将会使用WSASend、WSARecv,WSASendto、WSARecvFrom代替原先的Send、Recv...进行传输。这些WSA~系列的函数都包含一个Overlapped参数,可以理解为:通过Overlapped参数我们可以在使用WSASend\WSARecv\...等操作发出请求时将其投递到一个重叠模型上,而重叠模型又会与一个事件对象绑定,那此时我们在发出请求后就可以忙后面的事,等重叠操作完成后,绑定的对应的事件就会来通知我们操作完成。
      • WSARecv

        • 返回WSA_IO_PENDING说明操作成功,但I/O操作还没完成(因此需要绑定一个事件来通知我们何时完成)
        • 用例:
        • 其他仨函数大差不差,我理解都是借助重叠模型绑定事件,之后发起异步请求,在忙后面事的同时,等待重叠操作那边的事件通知。下面就看看等待事件触发的函数
    • WSAWaitForMultipleEvents --- WSA~系列的等待事件触发
        • 返回WSA_WAIT_TIMEOUT就说明事件还在跑,正常现象,需要做的就是继续wait。
        • 返回WSA_WAIT_FAILED说明出现错误,应检查cEvents和lphEvents。
        • 如果fWaitAll设置为FALSE,事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
      • Ps:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值(64),即说明单个WSAWaitForMultipleEvents最多只能等待64个事件,如果想同时等待多于64个事件,就得创建额外的工作者线程,就不得不去管理一个线程池。
    • WSAGetOverlappedResult --- 查询重叠操作的结果
      • 参数
        • 唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三个参数返回是 0 ,则说明通信对方已经关闭连接,我们这边的SOCKET, Event之类的也就可以关闭了。
    • 使用也相对简单,并非像我先前理解的必须要使用acceptEx,acceptEx可能更服务于完成端口吧,而且也不是必须的。基于事件通知的重叠操作,在accpet拿到客户端socket后,即可绑定到重叠模型,再将重叠模型绑定到事件对象,此时就得开两个线程,一个线程负责进行WSASend、WSARecv...,一个线程负责WSAWaitForMultipleEvents和等待到事件后WSAGetOverlappedResult 。当然也可以放到一个线程去用,但是搞这玩意儿不就是为了多线程异步操作么?
      • 要注意的是,一个socket的操作,对应一个WSAOVERLAPPED结构,对应一个WSAEVENT事件。对,是操作,如果一个socket同时进行了两个操作,同时只有一个操作,比如WSARecv,那么一个socket就可以对应一个WSAOVERLAPPED结构,但是如果一个socket上会有 WSARecv 和 WSASend 两个操作,那么一个SOCKET肯定就要对应两个WSAOVERLAPPED结构。所以有多少个SOCKET操作就会有多少个WSAOVERLAPPED结构,有多少个WSAOVERLAPPED结构就应该有多少个WSAEVENT事件。
  •