WinSock重叠I/O模型

一.重叠I/O模型的概念

当调用ReadFile()和WriteFile()时,如果最后一个参数lpOverlapped设置为NULL,那么线程就阻塞在这里,直到读写完指定的数据后,它们才返回。这样在读写大文件的时候,很多时间都浪费在等待ReadFile()和WriteFile()的返回上面。如果ReadFile()和WriteFile()是往管道里读写数据,那么有可能阻塞得更久,导致程序性能下降。

为了解决这个问题,Windows引进了重叠I/O的概念,它能够同时以多个线程处理多个I/O。其实你自己开多个线程也可以处理多个I/O,但是系统内部对I/O的处理在性能上有很大的优化。它是Windows下实现异步I/O最常用的方式。

Windows为几乎全部类型的文件提供这个工具:磁盘文件、通信端口、命名管道和套接字。通常,使用ReadFile()和WriteFile()就可以很好地执行重叠I/O。

重叠模型的核心是一个重叠数据结构。若想以重叠方式使用文件,必须用 FILE_FLAG_OVERLAPPED标志打开它,例如:

HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

如果没有规定该标志,则针对这个文件(句柄),重叠I/O是不可用的。如果设置了该标志,当调用ReadFile()和WriteFile()操作这个文件(句柄)时,必须为最后一个参数提供OVERLAPPED结构:

// WINBASE.H

typedef struct _OVERLAPPED{

DWORDInternal;

DWORDInternalHigh;

DWORDOffset;

DWORDOffsetHigh;

HANDLEhEvent; //关键的一个参数

}OVERLAPPED, *LPOVERLAPPED;

头两个32位的结构字InternalInternalHigh由系统内部使用;其次两个32位结构字OffsetOffsetHigh使得可以设置64位的偏移量,该偏移量是要文件中读或写的地方。

因为I/O异步发生,就不能确定操作是否按顺序完成。因此,这里没有当前位置的概念。对于文件的操作,总是规定该偏移量。在数据流下(如COM端口或socket),没有寻找精确偏移量的方法,所以在这些情况中,系统忽略偏移量。这四个字段不应由应用程序直接进行处理或使用,OVERLAPPED结构的最后一个参数是可选的事件句柄,当I/O完成时,该事件对象受信(signaled)。程序通过等待该对事件对象受信来做善后处理。

设置了OVERLAPPED参数后,ReadFile()/WriteFile()的调用会立即返回,这时候你可以去做其他的事(所谓异步),系统会自动替你完成ReadFile()/WriteFile()相关的I/O操作。你也可以同时发出几个ReadFile()/WriteFile()的调用(所谓重叠)。当系统完成I/O操作时,会将OVERLAPPED.hEvent置信,我们可以通过调用WaitForSingleObject/WaitForMultipleObjects来等待这个I/O完成通知,在得到通知信号后,就可以调用GetOverlappedResult来查询I/O操作的结果,并进行相关处理。由此可以看出,OVERLAPPED结构在一个重叠I/O请求的初始化及其后续的完成之间,提供了一种沟通或通信机制。注意OVERLAPPED结构的生存周期,一般动态分配,待I/O完成后,回收重叠结构。

以Win32重叠I/O机制为基础,自WinSock 2发布开始,重叠I/O便已集成到新的WinSock API中,比如WSARecv()/WSASend()。这样一来,重叠I/O模型便能适用于安装了WinSock 2的所有Windows平台。可以一次投递一个或多个WinSock I/O请求。针对那些提交的请求,在它们完成之后,应用程序可为它们提供服务(对I/O的数据进行处理)。

相应的,要想在一个套接字上使用重叠I/O模型来处理网络数据通信,首先必须使用 WSA_FLAG_OVERLAPPED这个标志来创建一个套接字。如下所示:

SOCKET s = WSASocket(AF_INET, SOCK_STEAM, 0, NULL, 0,  WSA_FLAG_OVERLAPPED);

创建套接字的时候,假如使用的是socket()函数,而非WSASocket()函数,那么会默认设置WSA_FLAG_OVERLAPPED标志。成功创建好了一个套接字,将其与一个本地接口绑定到一起后,便可开始进行这个套接字上的重叠I/O操作,方法是调用下述的WinSock 2函数,同时为它们指定一个WSAOVERLAPPED结构参数(#define WSAOVERLAPPED OVERLAPPED// WINSOCK2.H):

(1)WSASend()

(2)WSASendTo()

(3)WSARecv()

(4)WSARecvFrom()

(5)WSAIoctl()

(6)AcceptEx()

(7)TransmitFile()

若随一个WSAOVERLAPPED结构一起调用这些函数,函数会立即返回,无论套接字是否设为锁定模式。它们依赖于WSAOVERLAPPED结构来返回一个I/O请求操作的结果

比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,WinSock的重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。因为它和这4种模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据。也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。而这4种模型中,数据到达并拷贝到单套接字接收缓冲区(Per Socket Buffer)中,此时应用程序会被系统通知可以读入的字节数。当应用程序调用接收函数之后,数据才从单套接字缓冲区拷贝到应用程序的缓冲区。这样就减少了一次从I/O缓冲区到应用程序缓冲区的拷贝,差别就在于此

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值