手把手教你玩转SOCKET模型之重叠I/O篇(上)

身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人。我也希望大家能把自己的所学和他人一起分享,不要去鄙视别人索取时的贪婪,因为最应该被鄙视的是不肯付出时的吝啬。”

----- 题记  By PiggyXP(小猪)

  

  其实我首先应该道歉,因为7月份的时候曾信誓旦旦的说要写一套关于SOCKET所有模型的入门文章以及配套代码,不过没想到后天竟然被美女所迷出去度假了,刚刚回来不久。。。。。。-_-b其实那些模型的配套代码我已经基本写完了,只是没写配套文字,不过我想还是先从稍微难一点的模型写起吧,因为其他模型的入门毕竟要简单一些。

不过由于也是初学者,疏漏之处还望不吝指正。

本文凝聚着笔者心血,如要转载,请指明原作者及出处,谢谢!^_^

OK, Let’s go !  Have fun!! q^_^p

本文配套的示例源码下载地址

(VC.net 2003编写的多客户端MFC代码,配有详尽注释,只是简单的显示一下客户端发来的字符,稍加改进就是个聊天室了):

http://www.haha567.com/PiggyXP/OverlappedModel.rar

(unix系统,千万注意链接大小写)

非常感谢网络版的limin兄弟为我无偿提供的空间,以及在我学习过程中给我的大力帮助与支持,真的非常感谢他,感激涕零啊~~~~~T_T

也欢迎大家光临他的Blog一起讨论网络技术

http://blog.haha567.com/

(本文假设你已经具备用SOCKET简单模型编程的能力,如果对SOCKET一无所知请关注本系列其他文章)

目录:

1. 重叠模型的优点

2. 重叠模型的基本原理

3. 关于重叠模型的基础知识

4. 重叠模型的实现步骤

5. 多客户端情况的注意事项

 

 

一.             重叠模型的优点

  1. 可以运行在支持Winsock2的所有windows平台 ,而不像完成端口只是支持NT系统。
  2. 比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。

因为它和这4种模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。

而这4种模型种,数据到达并拷贝到单套接字接收缓冲区中,此时应用程序会被告知可以读入的容量。当应用程序调用接收函数之后,数据才从单套接字缓冲区拷贝到应用程序的缓冲区,差别就体现出来了。

  1. 从《windows网络编程》中提供的试验结果中可以看到,在使用了P4 1.7G Xero处理器(CPU很强啊)以及768MB的回应服务器中,最大可以处理4万多个SOCKET连接,在处理1万2千个连接的时候CPU占用率才40% 左右 ―― 非常好的性能,已经直逼完成端口了^_^

二.             重叠模型的基本原理

      说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提一下重叠模型的基本原理。

概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。

      需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):

  1. 事件对象通知(event object notification)
  2. 完成例程(completion routines) ,注意,这里并不是完成端口

而本文只是讲述如何来使用事件通知的的方法实现重叠IO模型,完成例程的方法准备放到下一篇讲 :) (内容太多了,一篇写不完啊) ,如没有特殊说明,本文的重叠模型默认就是指的基于事件通知的重叠模型。

既然是基于事件通知,就要求将windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),通俗一点讲,就是。。。。对了,忘了说了,既然要使用重叠结构,我们常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替换掉了, 它们的用法我后面会讲到,这里只需要注意一点,它们的参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据了。

      也许说了半天你还是不大明白,那就继续往后面看吧。。。。。。。-_-b,语言表达能力有限啊~~~

 

 

三.             关于重叠模型的基础知识

      下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。

  1. 1.      WSAOVERLAPPED结构

这个结构自然是重叠模型里的核心,它是这么定义的

typedef struct _WSAOVERLAPPED {
  DWORD Internal;
  DWORD InternalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  WSAEVENT hEvent;      // 唯一需要关注的参数,用来关联WSAEvent对象
       } WSAOVERLAPPED, *LPWSAOVERLAPPED;
我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:
WSAEVENT event;                   // 定义事件
WSAOVERLAPPED AcceptOverlapped ; // 定义重叠结构
event = WSACreateEvent();         // 建立一个事件对象句柄
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重叠结构
AcceptOverlapped.hEvent = event;    // Done !!
 
  1. 2.      WSARecv系列函数

在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用刀重叠结构嘛,它是这样定义的:

            int WSARecv(
                        SOCKET s,                      // 当然是投递这个操作的套接字
                        LPWSABUF lpBuffers,          // 接收缓冲区,与Recv函数不同
// 这里需要一个由WSABUF结构构成的数组
         DWORD dwBufferCount,        // 数组中WSABUF结构的数量
            LPDWORD lpNumberOfBytesRecvd// 如果接收操作立即完成,这里会返回函数调用
// 所接收到的字节数
          LPDWORD lpFlags,             // 说来话长了,我们这里设置为0 即可
          LPWSAOVERLAPPED lpOverlapped,  // “绑定”的重叠结构
          LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
                               // 完成例程中将会用到的参数,我们这里设置为 NULL
                );
返回值:
WSA_IO_PENDING : 最常见的返回值,这是说明我们的WSARecv操作成功了,但是
                    I/O操作还没有完成,所以我们就需要绑定一个事件来通知我们操作何时完成
 
举个例子:(变量的定义顺序和上面的说明的顺序是对应的,下同)
SOCKET s;
WSABUF DataBuf;           // 定义WSABUF结构的缓冲区
// 初始化一下DataBuf
#define DATA_BUFSIZE 5096
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer, DATA_BUFSIZE);
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;
// 建立需要的重叠结构
WSAOVERLAPPED AcceptOverlapped ;// 如果要处理多个操作,这里当然需要一个
// WSAOVERLAPPED数组
WSAEVENT event;     // 如果要多个事件,这里当然也需要一个WSAEVENT数组
                           // 需要注意的是可能一个SOCKET同时会有一个以上的重叠请求,
//  也就会对应一个以上的WSAEVENT
Event = WSACreateEvent();
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = event;     // 关键的一步,把事件句柄“绑定”到重叠结构上
// 作了这么多工作,终于可以使用WSARecv来把我们的请求投递到重叠结构上了,呼。。。。
WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes, 
&Flags, &AcceptOverlapped, NULL);
其他的函数我这里就不一一介绍了,因为我们毕竟还有MSDN这么个好帮手,而且在讲后面的完成例程和完成端口的时候我还会讲到一些 ^_^
 
  1. 3.      WSAWaitForMultipleEvents函数

熟悉WSAEventSelect模型的朋友对这个函数肯定不会陌生,不对,其实大家都不应该陌生,这个函数与线程中常用的WaitForMultipleObjects函数有些地方还是比较像的,因为都是在等待某个事件的触发嘛。

因为我们需要事件来通知我们重叠操作的完成,所以自然需要这个等待事件的函数与之配套。

                        DWORD WSAWaitForMultipleEvents(
                                  DWORD cEvents,                        // 等候事件的总数量
                                  const WSAEVENT* lphEvents,           // 事件数组的指针
                                  BOOL fWaitAll,          // 这个要多说两句:
                                                                  // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
                                                                  // FALSE则任何一个事件被传信函数都要返回
                                                                  // 我们这里肯定是要设置为FALSE的
                                  DWORD dwTimeout,    // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
                               // 如果设置为0,函数会立即返回
                            // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回
                            // 在这里不建议设置为WSA_INFINITE,因为。。。后面再讲吧..-_-b
                                  BOOL fAlertable       // 在完成例程中会用到这个参数,这里我们先设置为FALSE
                                );
返回值:
    WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
    WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
具体的例子就先不在这里举了,后面还会讲到
注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一个线程池,这一点就不如下一篇要讲到的完成例程模型了。

 

  1. 4.      WSAGetOverlappedResult函数

既然我们可以通过WSAWaitForMultipleEvents函数来得到重叠操作完成的通知,那么我们自然也需要一个函数来查询一下重叠操作的结果,定义如下

            BOOL WSAGetOverlappedResult(
                          SOCKET s,                   // SOCKET,不用说了
                          LPWSAOVERLAPPED lpOverlapped,  // 这里是我们想要查询结果的那个重叠结构的指针
                          LPDWORD lpcbTransfer,     // 本次重叠操作的实际接收(或发送)的字节数
                          BOOL fWait,                // 设置为TRUE,除非重叠操作完成,否则函数不会返回
                                                              // 设置FALSE,而且操作仍处于挂起状态,那么函数就会返回FALSE
                                                              // 错误为WSA_IO_INCOMPLETE
                                                              // 不过因为我们是等待事件传信来通知我们操作完成,所以我们这里设
                // 置成什么都没有作用…..-_-b  别仍鸡蛋啊,我也想说得清楚一些…
                          LPDWORD lpdwFlags       // 指向DWORD的指针,负责接收结果标志
                        );
这个函数没什么难的,这里我们也不需要去关注它的返回值,直接把参数填好调用就可以了,这里就先不举例了
唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三个参数返回是 0 ,则说明通信对方已经关闭连接,我们这边的SOCKET, Event之类的也就可以关闭了。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=114883

[推荐本文] [点击此处收藏本文]   发表于 2004年09月23日 10:39 PM

手把手教你玩转SOCKET模型之重叠I/O篇(上),布布扣,bubuko.com

时间: 2024-10-06 22:37:54

手把手教你玩转SOCKET模型之重叠I/O篇(上)的相关文章

手把手教你玩转SOCKET模型之重叠I/O篇(下)

四.     实现重叠模型的步骤 作 了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了.其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是只是学会 如何来使用它,却是真的不难,唯一需要理清思路的地方就是和大量的客户端交互的情况下,我们得到事件通知以后,如何得知是哪一个重叠操作完成了,继而知道 究竟该对哪一个套接字进行处理,应该去哪个缓冲区中的取得数据,everything will be OK^_^. 下面我们配合代码,来一步步的讲解如何亲手完成一个重叠模型.

手把手教你玩转CSS3 3D技术

手把手教你玩转 CSS3 3D 技术 要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上的2D事物,从而展现3D的效果.旋转则不再是2D平面上的旋转,而是三维坐标系的旋转,就包括X轴,Y轴,Z轴旋转.平移同理. 当然用理论来说明,估计你还不明白.下面是3个gif: 沿着X轴旋转 沿着Y轴旋转 沿着Z轴旋转 旋转应该没问题了,那理解平移起来就比较容易了,就是在在X轴.Y轴.z轴移动. 你可能

完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                              ----- By PiggyXP(小猪) 前 言 本系列里完毕port的代码在两年前就已经写好了,可是因为许久没有写东西了,不知该怎样提笔,所以这篇文档总是在酝酿之中--酝酿了两年之后,最终决定開始动笔了,但愿还不算晚-.. 这篇文档我很具体而且图文并茂的介绍了关于网络编程模型中完毕

手把手教你玩转阿里云双11拼团活动

各位新老用户们,如果您已经开了团,但是还不知道怎么玩?小编告诉来告诉你! 首先,进入活动主页面,点击[我要开团],选择您想要购买的云产品进行开团!如果您只想开团不想购买,也可以- 您开团后将享受以下福利: 福利1:拉新赢红包 团长开团后,可通过专属分享链接,邀请好友来参团.每成功邀请1个新用户参团购买您团里的云产品,您将获得1个拉新红包!红包从几十块到上千块不等! 最大的红包有1111元的现金红包,将即时发放到您的阿里云账户中!拉新人数越多,红包越多!如果您没有开团,参加了您好友开的团,您购买后

手把手教你玩转Git分布式版本控制系统!

目录 Git诞生历史 Git环境准备 Git安装部署 Git常用命令 Git基本操作 Git管理分支结构 Git管理标签 GitLab安装部署 GitHub托管服务 Git客户端工具 1 Git诞生历史 我 想大家还记得Linus torvalds在1991年时发布了Linux操作系统吧,从那以后Linux系统变不断发展壮大,因为Linux系统开源的特性,所以一直接受着来自 全球Linux技术爱好者的贡献,志愿者们通过邮件向Linus发送着自己编写的源代码文件,然后由Linus本人通过手工的方式

手把手教你玩转12306验证码的秘密!

12306相信对很多小伙伴都不陌生,假如问你对这个网站的印象的时候,你不是会立即想起那个坑爹的验证码,而正是这个验证码,也一时间成为小伙伴们讨论的话题,今天思梦PHP就给大家带来他的实现办法,纯属技术交流,有什么问题也欢迎前来探讨,当然了,12306还要承受亿级数据的并发,代码肯定也要高明的多,但是效果我们可以发挥我们的想象力有很多的实现方法,小编的只是其中的一种! 思路: 随机性,我们可以讲验证码划分几个类,然后随机挑选一个分类,之后创建一个分类的图片的数据表,提供分类下面有哪些图片展示,然后

Apache Beam实战指南 | 手把手教你玩转大数据存储HdfsIO

https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247494843&idx=2&sn=0dd20caec76e25b80e86ec5ce01868ef&chksm=fbea5574cc9ddc62927fce397c2e05b5e9ac0bbe1eef021286b2b728028bbfcb66cf31b1006c&scene=0&key=14400c6ba6ec8d49b83725e83e69b

小贞贞手把手教小白玩转jmeter<一>

一.Jmeter工具介绍 1.Jmeter是啥?又能干啥? Apache Jmeter是一款基于java的桌面应用,开源, 主要运用于压力测试.性能测试.接口测试及接口的自动化测试, 具体的实现:可大致分为: (1)录制或手动添加脚本 (2)优化脚本,参数化(即可以动态读取多个数据,例如100个用户登录,可将登录名进行参数化)设置断言(即进行检查确认是否完成了该请求或操作) (3)设置测试场景:设置线程数(即设置要运行脚本的虚拟用户数).循环次数等     (4)查看运行结果和结果分析 2.Jm

手把手教你玩转物业故障报修系统

目前新一代智能故障报修已深入物业,告别了到处找人的故障报修方式,我们可以感觉到,物联网的浪潮已经来临,小区物业已实现扫码进出大门,更有高档小区完成了刷脸开门.今天我们来谈谈物业的智能故障报修管理系统--青鸟报修云,了解一下如何通过扫码实现的物业故障报修的.让业主足不出户,省时省力完成故障提交. 以物业故障报修为例,简单讲解一下:新一代物业故障报修系统已发布,可实现物业扫码报修,过去业务找物业是件很麻烦的事情,停电或故障发生后不是上门找物业就是电话联系物业,最终一个问题始终拖延很久才可得到解决,现