四.Windows I/O模型之重叠IO(overlapped)模型

Posted 夜雨翛然

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四.Windows I/O模型之重叠IO(overlapped)模型相关的知识,希望对你有一定的参考价值。

1.适用于除Windows CE之外的各种Windows平台.在使用这个模型之前应该确保该系统安装了Winsock2.重叠模型的基本设计原理是使用一个重叠的数据结构,一次投递一个或多个Winsock I/O请求。在重叠模型中,收发数据使用WSA开头的函数。

2.WSA_FLAG_OVERLAPPED标志:要使用重叠模型。在创建套接字的时候,必须加上该标志。
SOCKET s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
假如使用的是socket函数,而非WSASocket函数,那么会默认设置WSA_FLAG_OVERLAPPED标志。
若随一个WSAFLAGOVERLAPPED结构一起调用这些以WSA开头的函数(AcceptEx和TRansmiteFile函数例外),函数会立即完成并返回,无论套接字是否设为阻塞模式

3.重叠模型在网络事件完成后,可以有两种方式通知应用程序:事件通知和完成例程

3.事件通知:事件对象与WSAOVERLAPPED进行绑定实现网络事件完成后通过事件进行通知。

4.WSAOVERLAPPED结构:
typedef struct WSAOverlapped
{
    DWORD Internal;
    DOWRD InternalHigh;
    DWORD Offset;
    DWORD OffsetHigh;
    WSAEVENT hEvent;
}WSAOVERLAPPED,FAR* LPWSAOVERLAPPED;
此处,程序员可以使用的是最后一个参数hEvent,其余的不用管。通过该参数,重叠结构可以与事件对象进行绑定,以实现网络事件完成后,通过事件对象进行通知应用程序。事件对象通知方式是通过创建一个事件对象,把该对象赋值给重叠结构的hEvent参数即可实现绑定。在此再次提醒大家注意:WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。

5.WSAGetOverlappedResult函数:重叠请求完成后,接着需要调用WSAGetOverlappedResult(取得重叠结构)函数,判断那个重叠调用到底是成功,还是失败.
BOOL WSAGetOverlappedResult(
    SOCKET s,//套接字
    LPWSAOVERLAPPED lpOverlapped,//重叠结构
    LPDWORD lpcbTransfer,//对应一个DWORD(双字节)变量,一次重叠实际传输(接收或者发送)的字节数
    BOOL fWait,//参数用于决定函数是否应该等待一次重叠操作完成。
    LPWORD lpdwFlags
    );
重叠操作完成,函数返回TRUE,否则,返回FALSE。返回FALSE通常都是有一下几种情况造成的.
(1)重叠I/O操作仍处在未完成状态
(2)重叠操作已经完成,但含有错误
(3)重叠操作的完成状态不可判决,因为在提供给函数WSAGetOverlappedResult的一个或多个参数中,存在着错误。
失败后,由lpcbTransfer参数指向的值不会进行更新,而且我们的应用程序应调用WSAGetLastError函数


6.基于事件通知的重叠模型编程步骤如下:
(1) 创建一个套接字,绑定本机端口,在指定的端口上监听连接请求。
(2)接受连接请求。
(3)为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后由函数WSAWaitForMultipleEvents使用。
(4)在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。注意函数通常会以失败告终,返回SOCKETERROR错误状态WSAIOPENDING(I/O操作尚未完成)。
(5)使用步骤3)的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。
(6)WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
(7)使用WSAGetOverlappedResult函数,判断重叠调用的返回状态是什么。
(8)在套接字上投递另一个重叠WSARecv请求。
(9)重复步骤5 ) ~ 8 )。

示例代码:

技术分享
 1 void main(void)
 2 {
 3     WSABUF databuf;
 4     DWORD eventTotal=0;
 5     WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
 6     WSAOVERLAPPED acceptOverlapped;
 7     SOCKET listensock,acceptsock;
 8 
 9 
10     //初始化工作和一般socket通信相同 
11     ...
12 
13     //接收连接
14     acceptsock=accept(listensock,NULL,NULL);
15 
16     //创建事件,绑定事件对象与重叠结构
17     eventArray[eventTotal]=WSACreateEvent();
18     ZeroMemory(&acceptOverlapped,sizeof(WSAOVERLAPPED));
19     acceptOverlapped.hEvent=eventArray[eventTotal];
20 
21     //数据缓冲区初始化
22     databuf.len=DATA_BUFSIZE;
23     databuf.buf=buffer;
24 
25     eventTotal++;
26 
27     //投递接收请求
28     WSARecv(acceptsock,&databuf,1,&recvBytes,&flags,&acceptOverlapped,NULL);
29 
30     while(1)
31     {
32         //监视事件对象状态
33         index=WSAWaitForMultipleEvents(eventTotal,eventArray,FALSE,WSA_INFINITE,FALSE);
34 
35         //人工充值事件
36         WSAResetEvent(eventArray[eventTotal-WSA_WAIT_EVENT_0]);
37 
38         //获取I/O操作的完成情况
39         WSAGetOverlappedResult(acceptsock,&acceptOverlapped,&bytesTransferred,FALSE,&flag);
40 
41         if(bytesTransferred==0)
42         {
43             closesocket(acceptsock);
44             WSACloseEvent(eventArray[eventTotal-WSA_WAIT_EVENT_0]);
45             return;
46         }
47 
48         //处理接收过来的数据
49         ...
50 
51         //再再次发送一个WSARecv请求
52         flag=0;
53         ZeroMemory(&acceptOverlapped,sizeof(WSAOVERLAPPED));
54 
55         databuf.LEN=DATA_BUFSIZE;
56         databuf.buf=buf;
57         WSARecv(acceptsock,&databuf,1,&recvbytes,&flag,&acceptOverlapped,NULL);
58     }
59     
60 }
View Code

注意:对于接受客户端连接的函数,还有一个AcceptEx函数,不过这个函数太过麻烦且对性能的提升没有太大的作用,暂时不打算学习

 

接下来学习基于完成例程的重叠IO模型:

 

















































以上是关于四.Windows I/O模型之重叠IO(overlapped)模型的主要内容,如果未能解决你的问题,请参考以下文章

Overlapped Model

windows重叠I/O模型

Socket编程模型之完成端口模型

SOCKET重叠I/O模型

Windows内核原理-同步IO与异步IO

Socket编程模型之完毕port模型