四.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模型:

7.基于完成例程的重叠模型的实现:
完成例程也就是回调函数。这种方式,就是设置一个回调函数,I/O请求完成后,自动调用回调函数即可。如果希望用完成例程为重叠I/O请求提供服务,在我们的应用程序中,必须为指定一个完成例程(回调函数),同时指定一个WSAOVERLAPPED结构。一个完成例程(回调函数)必须拥有下述函数原型:

1 void CALLBACK CompletionROUTINE(
2     DWORD dwError,//表明一个重叠操作的完成状态是什么
3     DWORD cdTransferred,//参数指定了在重叠操作期间,实际传输的字节量是多大
4     LPWSAOVERLAPPED lpOverlapped,//重叠结构
5     DWORD dwFlags//目前尚未使用,应设为0
6     );
View Code

8.基于完成例程和基于事件对象两种重叠模型之间的差异:在使用完成重叠模型时,WSAOVERLAPPED结构的事件字段hEvent并未使用;也就是说,我们不可将一个事件对象同重叠请求关联到一起。用完成例程发出了一个重叠I/O请求之后,作为我们的调用线程,一旦完成,它最终必须为完成例程提供服务。这样一来,便要求我们将自己的调用线程置于一种“可警告的等待状态”。并在I/O操作完成后,对完成例程加以处理。WSAWaitForMultipleEvents函数可用来将我们的线程置于一种可警告的等待状态。这样做的缺点在于,我们还必须有一个事件对象可用于WSAWaitForMultipleEvents函数。假定应用程序只用完成例程对重叠请求进行处理,便不可能有任何事件对象需要处理。作为一种变通方法,我们的应用程序可用Win32的SleepEx函数将自己的线程置为一种可警告等待状态。当然,亦可创建一个伪事件对象,不将它与任何东西关联在一起。假如调用线程经常处于繁忙状态,而且并不处在一种可警告的等待状态,那么根本不会有投递的完成例
程会得到调用。
SleepEx函数的行为实际上和WSAWaitForMultipleEvents差不多,只是它不需要任何事件对象。
9.SleepEx函数:

1 DWORD SleepEx(
2     DWORD dwMillisecond,//等待时间
3     BOOL bAlertable,//指定完成例程的执行方式
4     )
View Code

第二个参数是否置于警觉状态,如果为FALSE,则一定要等待超时时间完毕之后才会返回,这里我们是希望重叠操作一完成就能返回,所以我们要设置为TRUE

10.window下,管理重叠io有三种方式:基于事件的重叠io模型,基于完成例程的重叠io模型,完成端口模型,相比之下,在性能方面前两种方式没有区别,第三种完成端口模型性能最优。因为前两种都需要自己来管理任务的分派,而完成端口是操作系统来管理任务的分派。虽然第一二中Io模型在性能上没有区别。但是,基于事件的重叠io模型要受到最多64个等待事件的限制。完成例程则没有这种限制。
11.完成历程的基本原理:在基于事件的重叠io模型中,io操作完成后,系统会以事件的方式通知应用程序。而对于完成例程来说,系统会在io操作完成后,直接调用应用程序提供的回调函数。区别也就仅此而已。对于完成例程,我们可以用一种非常形象的话来进行表述:完成例程的处理过程,也就像我们告诉系统,说“我想要在网络上接收网络数据,你去帮我办一下”(投递WSARecv操作),“不过我并不知道网络数据合适到达,总之在接收到网络数据之后,你直接就调用我给你的这个函数(比如_CompletionProess),把他们保存到内存中或是显示到界面中等等,全权交给你处理了”,于是乎,系统在接收到网络数据之后,一方面系统会给我们一个通知,另外同时系统也会自动调用我们事先准备好的回调函数,就不需要我们自己操心了。
12.完成例程的函数基本上和基于事件的io模型的函数是相同的。只是在这里我们需要添加一个回调函数。回调函数原型如下:(函数名字随便起,但是参数类型不能错):

1     Void CALLBACK _CompletionRoutineFunc(
2     DWORD dwError, // 标志咱们投递的重叠操作,比如WSARecv,完成的状态是什么
3     DWORD cbTransferred, // 指明了在重叠操作期间,实际传输的字节量是多大
4     LPWSAOVERLAPPED lpOverlapped, // 参数指明传递到最初的IO调用内的一个重叠结构
5     DWORD dwFlags  // 返回操作结束时可能用的标志(一般没用)
6 
7 );
View Code

13.定义了回调函数之后,还需要把回调函数与系统绑定之后,网络操作完成后才会自动调用回调函数。例如WSARecv绑定过程如下:

1     int WSARecv(
2     SOCKET s,            // 当然是投递这个操作的套接字
3     LPWSABUF lpBuffers,  // 接收缓冲区,与Recv函数不同,这里需要一个由WSABUF结构构成的数组
4     DWORD dwBufferCount, // 数组中WSABUF结构的数量,设置为1即可
5     LPDWORD lpNumberOfBytesRecvd,//所接收到的字节数
6     LPDWORD lpFlags,             // 说来话长了,我们这里设置为0 即可
7     LPWSAOVERLAPPED lpOverlapped,  // “绑定”的重叠结构
8     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //回调函数的指针
9     ); 
View Code

    //注意:绑定动作就是通过填充最后一个参数进行实现的。
14.完成例程的实现步骤:
(1)创建一个套接字,开始在指定的端口上监听连接请求
(2)接收连接请求即调用accept函数,生成一个新的socket套接字
(3)准备重叠结构:需要注意的是,一个套接字对应一个或者多个io请求,一个io请求对应一个重叠结构
    WSAOVERLAPPED AcceptOverlapped;  
    ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));      // 置零
(4)投递io请求(此处为WSARecv请求),使用重叠结构,回调函数填充参数:
    WSARecv(AcceptSocket,&DataBuf,1,&dwRecvBytes, &Flags,&AcceptOverlapped,_CompletionRoutine)
(5)重叠操作绑定完成后,接下来就是等待io请求的完成结果。有两种方式可以实现:调用WSAWaitForMultipleEvents函数或者SleepEx函数。

1     通过WaitForMultipleEvents函数等待
2     WSAEVENT EventArray[1];      
3     EventArray[0] = WSACreateEvent();// 建立一个事件
4     // 然后就等待重叠请求完成就可以了,注意保存返回值,这个很重要
5     DWORD dwIndex = WSAWaitForMultipleEvents(1,EventArray,FALSE,WSA_INFINITE,TRUE);//注意fAlertable 参数即该函数最后                                            一个参数一定要设置为 TRUE
6     通过SleepEx函数进行等待,效果是一样的:
7     DWORD SleepEx(DWORD dwMilliseconds,  // 等待的超时时间,如果设置为INFINITE就会一直等待下去
8               BOOL   bAlertable   // 是否置于警觉状态,如果为FALSE,则一定要等待超时时间完毕之后才会返回,这里我们是希望重叠操作一完成就能返回,所以同(1)一样,我们一定要设置为TRUE
9               ); 
View Code

(6)通过等待函数的返回值取得io请求的结果:在等待函数中,正常情况下,在操作完成之后,应该是返回WAIT_IO_COMPLETION,如果返回的是 WAIT_TIMEOUT,则表示等待设置的超时时间到了,但是重叠操作依旧没有完成,应该通过循环再继续等待。如果是其他返回值,那就坏事了,说明网络通信出现了其他异常,程序就可以报错退出了

 1     if(dwIndex == WAIT_IO_COMPLETION)
 2     {
 3     TRACE("重叠操作完成.../n");
 4     }
 5     else if( dwIndex==WAIT_TIMEOUT )
 6     {
 7     TRACE(“超时了,继续调用等待函数”);
 8     }
 9     else
10     {
11         TRACE(“废了…”);
12     }
View Code

(7)投递下一个io请求,重复4-7
15.读取io请求的最终结果数据:在WSARecv调用的时候,是传递了一个WSABUF的变量的,用于保存网络数据,而在我们写的完成例程回调函数里面,就可以取到客户端传送来的网络数据了。因为系统在调用我们完成例程函数的时候,其实网络操作已经完成了,WSABUF里面已经有我们需要的数据了,只是通过完成例程来进行后期的处理
示例代码:

 1 //完成例程实现重叠io模型伪代码
 2 SOCKET acceptSock;
 3 WSABUF dataBuf;
 4 
 5 void main()
 6 {
 7     WSAOVERLAPPED overlapped;
 8     //1.初始化
 9     //...
10 
11     //2.接收连接请求
12     acceptSock=accept(listenSock,NULL,NULL);
13 
14     //3.初始化重叠结构
15     UINT flag=0;
16     ZeroMemory(&overlapped,sizeof(WSAOVERLAPPED));
17     dataBuf.len=DATA_BUFSIZE;
18     dataBuf.buf=buf;
19 
20     if (WSARecv(acceptSock,&dataBuf,1,&recvBytes,&flag,&overlapped,workroutine)==SOCKET_ERROR)//最后一个参数时回调函数地址
21     {
22         if(WSAGetLastError()!=WSA_IO_PENDING)
23         {
24             printf("WSARecv() failed with error %d\n",WSAGetLastError());
25             return;
26         }
27     }
28     
29     //创建事件
30     eventArray[0]=WSACreateEvent();
31     while (true)
32     {
33         int index=WSAWaitForMultipleEvents(1,eventArray,FALSE,WSA_INFINITE,TRUE);//最后一个参数最好为true
34         if (index==WAIT_IO_COMPLETION)//io请求完成
35         {
36             break;
37         }
38         else//io请求出错
39         {
40             return;
41         }
42     }
43     //调用回调函开始进行处理
44 }
45 
46 void CALLBACK WorkRoutine(DWORD error,DWORD bytesTransferred,LPWSAOVERLAPPED overlapped,DWORD inflag)
47 {
48     DWORD sendBytes,recvBytes;
49     DWORD flags;
50 
51     if(error!=0||bytesTransferred==0)
52     {
53         closesocket(acceptSock);
54         return;
55     }
56 
57     flags=0;
58 
59     ZeroMemory(&overlapped,sizeof(WSAOVERLAPPED));
60     dataBuf.len=DATA_BUFSIZE;
61     dataBuf.data=buf;
62 
63     if (WSARecv(acceptSock,&dataBuf,1,&recvBytes,&flag,&overlapped,workroutine)==SOCKET_ERROR)//最后一个参数时回调函数地址
64     {
65         if(WSAGetLastError()!=WSA_IO_PENDING)
66         {
67             printf("WSARecv() failed with error %d\n",WSAGetLastError());
68             return;
69         }
70     }
71 }
View Code

 

转载于:https://www.cnblogs.com/HPAHPA/p/7819498.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vscode怎样导入数据_【Python开发】用VSCode+Jupyter notebook 编写 Python

版权声明:小博主水平有限,希望大家多多指导。本文仅代表作者本人观点。1、过去,想要在 VSCode 中运行 Jupyter notebook 需要安装一个 Neuron 扩展,我也装过,感觉很强大、很方便。不过现在,VSCode 中 Pytho…

springboot怎么杀进程_全新Steam在线游戏 Among us太空狼人杀攻略

众多游戏爱好者已加入我们!带你发现好游戏!休闲娱乐小游戏!点击下方↓↓↓↓"开始游戏",赶紧进入吧!!戳“开始游戏”玩百款火爆小游戏!《Among us》游戏好玩吗?《Among us…

kafka 怎么样连接图形化界面_从零开始搭建Kafka+SpringBoot分布式消息系统

前言由于kafka强依赖于zookeeper,所以需先搭建好zookeeper集群。由于zookeeper是由java编写的,需运行在jvm上,所以首先应具备java环境。(ps:默认您的centos系统可联网,本教程就不教配置ip什么的了)(ps2:没有…

《Iterative-GAN》的算法伪代码整理

花了一下午时间整理本人的论文Iterative-GAN的算法伪代码,由于篇幅较长,投会议方面的文章就不加入了,以后如果投期刊再说。留此存档。 转载于:https://www.cnblogs.com/punkcure/p/7821031.html

h5能调取摄像头吗_高质感的国产中型车,实力能比肩本田雅阁吗?带你看红旗H5...

中国品牌的豪华中型车,带你看红旗H5伴随着经济的快速发展,大家的钱包现在也是越来越鼓,也开始向往更加美好的生活。曾经很多人买车都是为了满足基本的代步需求,如今也开始在车辆的品质与行驶质感上有了更高要求。而为了迎合市场变…

lstm网络_LSTM(长短期记忆网络)

在上篇文章一文看尽RNN(循环神经网络)中,我们对RNN模型做了总结。由于RNN也有梯度消失的问题,因此很难处理长序列的数据,大牛们对RNN做了改进,得到了RNN的特例LSTM(Long Short-Term Memory),它可以避免常规RNN的梯度消…

ant接口用什么天线_手机听收音机时,为什么必须用耳机作为天线?

名侦探柯基-十万个为什么 第七十六期起因,观看活着韩国丧尸电影时的一幕,刘亚仁想听电台广播,却无奈于所有设备都是无线的,由此疑惑到,只有插入有线的耳机,才能收听广播吗?耳机线就是天线&#…

qt c++ 图片预览_Qt多语言国际化

Qt附加工具介绍Qt Assistant(Qt助手)Qt Linguist(Qt语言家)Qt Designer(Qt设计师)Qt AssistantQt Assistant是可配置且可重新发布的文档阅读器,可以方便地进行定制并与Qt应用程序一起重新发布。Qt Assistan…

Icon+启动图尺寸

1、LaunchImage 启动图 命名格式: 1x -> xxx.png 2x -> xxx2x.png Retina 4 -> xxx2x.png     转载于:https://www.cnblogs.com/z-z-z/p/7828082.html

智商情商哪个重要_《所谓逆商高,就是心态好》:逆商,比情商和智商更重要...

所谓“逆商”,是指人们遇到逆境时的应对能力,即战胜挫折、摆脱困境和超越困难的能力。我们一生会面临各种各样的难题,也许是考试失利,也许是和心爱的人分离,也许是工作上竞争失败……在失意的时候你会做何选择&#xf…

mysql 排名_学会在MySQL中实现Rank高级排名函数,所有取前几名问题全部解决.

MySQL中没有Rank排名函数,当我们需要查询排名时,只能使用MySQL数据库中的基本查询语句来查询普通排名。尽管如此,可不要小瞧基础而简单的查询语句,我们可以利用其来达到Rank函数一样的高级排名效果。在这里我用一个简单例子来实现…

意大利_【解读】去意大利留学,一定要学意大利语吗?意大利语难吗?

喜欢意大利,想去意大利留学,但不想学意大利语可以吗?意大利语太难了,听说有英授专业(本来就要学英语、考雅思所以不担心英语)……问题来了去意大利留学,选择英授专业的话还需要学意大利语吗?我们一点点剖析…

MD5、SHA1、SHA256的简单讲解

简述: 最近在研究系统以及驱动,当下载比较大的文件时总会提供SHA1或者SHA256,下载结束后使用校验工具得到的值与它进行比对来判断下载是否成功。 使用工具校验 certutil -hashfile 文件名 sha1/sha256/md5正文: MD5、SHA1、SHA256这些都被称为 哈希…

java swarm集群_52个Java程序员不可或缺的 Docker 工具

Docker工具分类列表编排和调度持续集成/持续部署(CI / CD)监控记录安全存储/卷管理联网服务发现构建管理编排和调度 1. KubernetesKubernetes是市场上最实用的最受欢迎的容器编排引擎。最初作为一个Google项目开始,成千上万的团队使用它来部署生产中的容器。谷歌声称…

comsol显示电场计算结果_在 COMSOL 中构建磁流体动力学多物理场模型

COMSOL Multiphysics 软件中的模型都是从零开始构建的,软件支持多物理场,因此用户可以按照自己的意愿轻松地组合代表不同物理场现象的模型。有时这可以通过使用软件的内置功能来实现,但有些情况下,用户需要做一些额外的工作。我们…

RGB转LAB色彩空间

https://www.cnblogs.com/hrlnw/p/4126017.html 1.原理 RGB无法直接转换成LAB,需要先转换成XYZ再转换成LAB,即:RGB——XYZ——LAB 因此转换公式分两部分: (1)RGB转XYZ 假设r,g,b为像素三个通道,…

React- jsx的使用可以渲染html标签 或React组件

React 的 JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。既渲染html标签需要使用小写字母开头的标签名而渲染本地React组件需要使用大写字母开头的标签名 注意: 由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代&…

bigdecimal保留4位小数_四年级数学小数的加减乘法知识点汇总,带练习!

张老师 - 4年级(多品小学教育)顺城中心小学郭老师和环县虎洞中心小学谭老师需要的这份学习资料现在分享。本资料已制作电子版下载码是:76qa32vd《小数的加减乘法》知识点一、小数加、减法的计算法则(1)小数点要对齐,也就是相同数位要对齐;相同…

vs未指定启动文件_高效开发利器之自定义模板文件

简介越来越多的设计模式, 最佳实践, 优化了软件开发方式, 提高了软件质量, 但是也带来了大量的类似的文件. 比如说: 现在都使用三层模式来开发Web程序, 都会有服务层(Service), DI的流行, 每个服务类都会自动注入一些固定的对象, 日志对象, 缓存对象等等. 实体类(Entity)到DTO对…

react-router 4.0 学习笔记

1、安装react-router-dom 2、页面上要使用的时候要引入  import {BrowserRouter as Router,Route,Link } from react-router-dom 3、使用的时候要在外层包一个<Router> 4、param 在路径上如果带有/:id类似这种的&#xff0c;要取值的时候&#xff0c;match.params.id取…