SOCKET编程进阶之Overlapped I\O事件通知模型

转载

SOCKET编程进阶之Overlapped I\O事件通知模型

WINSOCK I\O模型有六种: 
一:select模型 
二:WSAAsyncSelect模型 
三:WSAEventSelect模型 
四:Overlapped I/O 事件通知模型 
五:Overlapped I/O 完成例程模型 
六:完成端口IOCP模型 
且一个比一个完善,一个比一个高深。最好用的莫过于完成端口,但惋惜的是只有NT、2000的系统才支持这种功能。心痛之余,咱们只能寄但愿于Overlapped I/O模型。 
下面,咱们来详细分析一下重叠模型: 编程


1、重叠模型的优势 
1)能够运行在支持Winsock2的全部Windows平台 ,而不像完成端口只是支持NT系统。 
2)比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。 
由于它和这4种模型不一样的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,若是应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。 
而这4种模型种,数据到达并拷贝到单套接字接收缓冲区中,此时应用程序会被告知能够读入的容量。当应用程序调用接收函数以后,数据才从单套接字缓冲区拷贝到应用程序的缓冲区,差异就体现出来了。 
3)从《windows网络编程》中提供的试验结果中能够看到,在使用了P4 1.7G Xero处理器(CPU很强啊)以及768MB的回应服务器中,最大能够处理4万多个SOCKET链接,在处理1万2千个链接的时候CPU占用率才40% 左右 ―― 很是好的性能,已经直逼完成端口了^_^ windows


2、重叠模型的基本原理 
说了这么多的好处,你必定也跃跃欲试了吧,不过咱们仍是要先提一下重叠模型的基本原理。 
归纳一点说,重叠模型是让应用程序使用重叠数据结构(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之后就能够“不劳而获”,等到重叠操做完成之后,天然会有与之对应的事件来通知咱们操做完成,而后咱们就能够来根据重叠操做的结果取得咱们想要德数据了。 数组


3、关于重叠模型的基础知识 
下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。 
1)WSAOVERLAPPED结构 
这个结构天然是重叠模型里的核心,它是这么定义的 服务器

 

咱们须要把WSARecv等操做投递到一个重叠结构上,而咱们又须要一个与重叠结构“绑定”在一块儿的事件对象来通知咱们操做的完成,看到了和hEvent参数,不用我说大家也该知道如何来来把事件对象绑定到重叠结构上吧?大体以下: 
 网络


2)WSARecv系列函数 
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,由于要用刀重叠结构嘛,它是这样定义的: 
 

返回值: 
WSA_IO_PENDING : 最多见的返回值,这是说明咱们的WSARecv操做成功了,可是I/O操做尚未完成,因此咱们就须要绑定一个事件来通知咱们操做什么时候完成 
其余的函数我这里就不一一介绍了,由于咱们毕竟还有MSDN,并且在讲后面的完成例程和完成端口的时候我还会讲到一些 ^_^ 

3)WSAWaitForMultipleEvents函数 
熟悉WSAEventSelect模型的朋友对这个函数确定不会陌生,不对,其实你们都不该该陌生,这个函数与线程中经常使用的WaitForMultipleObjects函数有些地方仍是比较像的,由于都是在等待某个事件的触发嘛。 
由于咱们须要事件来通知咱们重叠操做的完成,因此天然须要这个等待事件的函数与之配套。 
 

返回值: 
WSA_WAIT_TIMEOUT :最多见的返回值,咱们须要作的就是继续Wait 
WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效 
若是事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,可是这个索引值须要减去预约义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。 
具体的例子就先不在这里举了,后面还会讲到 
注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,若是想同时等待多于64个事件,就要 建立额外的工做者线程,就不得不去管理一个线程池,这一点就不以下一篇要讲到的完成例程模型了。 

4)WSAGetOverlappedResult函数 
既然咱们能够经过WSAWaitForMultipleEvents函数来获得重叠操做完成的通知,那么咱们天然也须要一个函数来查询一下重叠操做的结果,定义以下 
 
1 BOOL WSAGetOverlappedResult(
2                           SOCKET s,                   // SOCKET,不用说了
3                           LPWSAOVERLAPPED lpOverlapped,  // 这里是咱们想要查询结果的那个重叠结构的指针
4                           LPDWORD lpcbTransfer,     // 本次重叠操做的实际接收(或发送)的字节数
5                           BOOL fWait,                // 设置为TRUE,除非重叠操做完成,不然函数不会返回,设置FALSE,并且操做仍处于挂起状态,那么函数就会返回FALSE,错误为WSA_IO_INCOMPLETE, 不过由于咱们是等待事件传信来通知咱们操做完成,因此咱们这里设,置成什么都没有做用…..-_-b  别仍鸡蛋啊,我也想说得清楚一些…
6                           LPDWORD lpdwFlags       // 指向DWORD的指针,负责接收结果标志
7                         );

这个函数没什么难的,这里咱们也不须要去关注它的返回值,直接把参数填好调用就能够了,这里就先不举例了 
惟一须要注意一下的就是若是WSAGetOverlappedResult完成之后,第三个参数返回是 0 ,则说明通讯对方已经关闭链接,咱们这边的SOCKET, Event之类的也就能够关闭了。数据结构

实现重叠模型的步骤做了这么多的准备工做,费了这么多的笔墨,咱们终于能够开始着手编码了。其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,可是只是学会如何来使用它,倒是真的不难,惟一须要理清思路的地方就是和大量的客户端交互的状况下,咱们获得事件通知之后,如何得知是哪个重叠操做完成了,继而知道究竟该对哪个套接字进行处理,应该去哪一个缓冲区中的取得数据,everything will be OK^_^。下面咱们配合代码,来一步步的讲解如何亲手完成一个重叠模型。 
下面是我写的一个例子,用的标准C\C++,能够直接编译。 
因为WSAWaitForMultipleEvents最多只能同时等待64个消息,因此两个线程最多支持64个链接,若要更多能够在开一个线程,达到128个链接。以此类推,成线性增加。 
但线程过多的话,因为CPU忙于在线程上下文之间的切换,也会影响程序的性能,因此这种模式,仍是不太适合很是多的链接数,如10000多个链接就不行了,这时,咱们只能用后面的完成例程或完成端口了,这也正是它的弊端所在。 app

相关文章
相关标签/搜索