您的位置:首页 > 其它

WinSock重叠I/O模型

2013-05-16 16:46 190 查看
一.重叠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位的结构字Internal和InternalHigh由系统内部使用;其次两个32位结构字Offset和OffsetHigh使得可以设置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缓冲区到应用程序缓冲区的拷贝,差别就在于此
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: