Windows下重叠IO模型_第1页
Windows下重叠IO模型_第2页
Windows下重叠IO模型_第3页
Windows下重叠IO模型_第4页
Windows下重叠IO模型_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Windows下重叠I/O模型(转贴)重叠模型的优点可以运行在支持Winsock2的所有Windows平台,而不像完成端口只是支持NT 系统。比起阻塞、select、WSAAsyncSelect 以及 WSAEventSelect 等模型,重叠 I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。因为它和这4种模型不同的是,使用重叠模型的应用程序通 知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大 小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递 的缓冲区。而这4种模型种,数据到达并拷贝到单套接字接收缓冲区中,此时应用程序 会

2、被告知可以读入的容量。当应用程序调用接收函数之后,数据才从单套接字缓 冲区拷贝到应用程序的缓冲区,差别就体现出来了。从windows网络编程中提供的试验结果中可以看到,在使用了 P4 1.7G Xero 处理器(CPU很强啊)以及768MB的回应服务器中,最大可以处理4万多个SOCKET 连接,在处理1万2千个连接的时候CPU占用率才40%左右一一非常好的性 能,已经直逼完成端口。重叠模型的基本原理说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提 一下重叠模型的基本原理。概括一点说,重叠模型是让应用程序使用重叠数据结构 (WSAOVERLAPPED),一次投递一个或多个Winsock

3、 I/O请求。针对这些提交的请 求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来 处理这些数据了。需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况 (就是说接到重叠操作完成的通知):事件对象通知(event object notification)完成例程(completion routines),注意,这里并不是完成端口而本文只是讲述如何来使用事件通知的的方法实现重叠IO模型,完成例程 的方法准备放到下一篇讲:)(内容太多了,一篇写不完啊),如没有特殊说明,本文的重叠模型默认就是指的基于事件通知的重叠模型。既然是基于事件通知,就要求将Windows事件对象与W

4、SAOVERLAPPED结构关 联在一起(WSAOVERLAPPED结构中专门有对应的参数),通俗一点讲,就是。 对了,忘了说了,既然要使用重叠结构,我们常用的send, sendto, recv, recvfrom 也都要被 WSASend, WSASendto, WSARecv, WSARecvFrom 替换掉了,它 们的用法我后面会讲到,这里只需要注意一点,它们的参数中都有一个 Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定” 到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中 重叠结构又要与Windows的事件对象“绑定”在一起,

5、这样我们调用完WSARecv 以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来 通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据 了。也许说了半天你还是不大明白,那就继续往后面看吧。 -_-b,语言表达能力有限啊关于重叠模型的基础知识下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几 个关键函数。1. WSAOVERLAPPED 结构这个结构自然是重叠模型里的核心,它是这么定义的typedef struct _WSAOVERLAPPED (DWORD Internal;DWORD InternalHigh;DWORD Offset;DWORD

6、 OffsetHigh;WSAEVENT hEvent;/唯一需要关注的参数,用来关联WSAEvent 对象 WSAOVERLAPPED, *LPWSAOVERLAPPED;我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结 构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数, 不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下: WSAEVENT event;/ 定义事件WSAOVERLAPPED AcceptOverlapped ; / 定义重叠结构event = WSACreateEvent();/建立一个事件对象句柄Zer

7、oMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED); / 初始化重叠结 构AcceptOverlapped.hEvent = event; / Done !2. WSARecv系列函数在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用刀重 叠结构嘛,它是这样定义的:int WSARecv(SOCKET s,/当然是投递这个操作的套接字一LPWSABUF lpBuffers,/ 接收缓冲区,与Recv函数不同/需要一个由WSABUF结构构成的数组DWORD dwBufferCount,/ 数组中 WSABUF结构的数量LPDWORD

8、 lpNumberOfBytesRecvd, / 如果接收操作立即完成,这里会返回函数调用接收到的字节数LPDWORD lpFlags,/ 说来话长了,我们这里设置为0即可LPWSAOVERLAPPED lpOverlapped, / “绑定”的重叠结 构LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine/完成例程中将会用到的参数,我们这里设置为NULL);返回值:WSA_IO_PENDING :最常见的返回值,这是说明我们的WSARecv操作成功了,但是I/O操作还没有完成,所以我们就需要绑定一个事件来通知我们操作何时完成举个例子:(变

9、量的定义顺序和上面的说明的顺序是对应的,下同)SOCKET s;WSABUF DataBuf;/定义WSABUF结构的缓冲区/初始化一下DataBuf#define DATA_BUFSIZE 5096char bufferDATA_BUFSIZE;ZeroMemory(buffer, DATA_BUFSIZE);DataBuf.len = DATA_BUFSIZE;DataBuf.buf = buffer;DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;/建立需要的重叠结构WSAOVERLAPPED AcceptOverlapped ;/

10、如果要处理多个操作,这里当然需要一 个/ WSAOVERLAPPED 数组WSAEVENT event;/如果要多个事件,这里当然也需要一个WSAEVENT数组/需要注意的是可能一个SOCKET同时会有一个以上的重叠请求,/也就会对应一个以上的WSAEVENTEvent = WSACreateEvent();ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED);AcceptOverlapped.hEvent = event;/ 关键的一步,把事件句柄“绑定”到重叠结构上/作了这么多工作,终于可以使用WSARecv来把我们的请求投递到重叠结构上

11、了,呼。WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,&Flags, &AcceptOverlapped, NULL);其他的函数我这里就不一一介绍了,因为我们毕竟还有MSDN这么个好帮手,而 且在讲后面的完成例程和完成端口的时候我还会讲到一些”_”WSAWaitForMultipleEvents 函数熟悉WSAEventSelect模型的朋友对这个函数肯定不会陌生,不对,其实大家都 不应该陌生,这个函数与线程中常用的WaitForMultipleObjects函数有些地方 还是比较像的,因为都是在等待某个事件的触发嘛。因为我们需要事件来通知

12、我们重叠操作的完成,所以自然需要这个等待事件的函 数与之配套。DWORD WSAWaitForMultipleEvents(DWORD cEvents,/等候事件的总数量const WSAEVENT* lphEvents,/事件数组的指针BOOL fWaitAll,/ 设置为TRUE,则事件数组中所有事件被传信的时候函数才会返回/ FALSE则任何一个事件被传信函数都要返回 /我们这里肯定是要设置为FALSE的DWORD dwTimeout, /超时时间,如果超时,函数会返回WSA_WAIT_TIMEOUT/如果设置为0,函数会立即返回/如果设置为WSA_INFINITE只有在某一个事件被传信

13、后才会返回/在这里不建议设置为WSA_INFINITE,因为。后面再讲吧.-_-bBOOL fAlertable/ 在完成例程中会用到这个参数,这里我们先设置为FALSE);返回值:WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续WaitWSA_WAIT_FAILED :出现了错误,请检查 cEvents 和 lphEvents 两个参 数是否有效如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这 个索引值需要减去预定义值WSA_WAIT_EVENT_0才是这个事件在事件数组中的 位置。具体的例子就先不在这里举了,后面还会讲到注意:WSAWaitFo

14、rMultipleEvents 函数只能支持由 WSA_MAXIMUM_WAIT_EVENTS 对象定义的一个最大值,是64,就是说WSAWaitForMultipleEvents只能等待 64个事件,如果想同时等待多于64个事件,就要创建额外的工作者线程,就 不得不去管理一个线程池,这一点就不如下一篇要讲到的完成例程模型了。WSAGetOverlappedResult 函数既然我们可以通过WSAWaitForMultipleEvents函数来得到重叠操作完成的通知,那么我们自然也需要一个函数来查询一下重叠操作的结果,定义如下BOOL WSAGetOverlappedResult(SOCKET

15、 s,/ SOCKET,不用说了LPWSAOVERLAPPED lpOverlapped, / 这里是我们想要查询结果的那个重叠结构的指针LPDWORD lpcbTransfer,/ 本次重叠操作的实际接收(或发送)的字节数BOOL fWait,/设置为TRUE,除非重叠操作完成,否则函数不会返回/设置FALSE,而且操作仍处于挂起状态,那么函数就会返回FALSE / 错误为 WSA_IO_INCOMPLETE /不过因为我们是等待事件传信来通知我们操作完成,所以我们这里设置成什么 都没有作用LPDWORD IpdwFlags/ 指向DWORD的指针,负责接收结果标志);这个函数没什么难的,这

16、里我们也不需要去关注它的返回值,直接把参数填好调 用就可以了,这里就先不举例了唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三个参数 返回是0,则说明通信对方已经关闭连接,我们这边的SOCKET, Event之类的 也就可以关闭了四。实现重叠模型的步骤作了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了。 其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是 只是学会如何来使用它,却是真的不难,唯一需要理清思路的地方就是和大量的 客户端交互的情况下,我们得到事件通知以后,如何得知是哪一个重叠操作完成 了,继而知道究竟该对哪一个

17、套接字进行处理,应该去哪个缓冲区中的取得数据, everything will be OK。下面我们配合代码,来一步步的讲解如何亲手完成一个重叠模型。【第一步】定义变量#define DATA_BUFSIZE4096/ 接收缓冲区大小SOCKETListenSocket,/ 监听套接字AcceptSocket;/与客户端通信的套接字WSAOVERLAPPED AcceptOverlapped;/ 重叠结构一个WSAEVENT EventArrayWSA_MAXIMUM_WAIT_EVENTS;/用来通知重叠操作完成的事件句柄数组WSABUFDataBufDATA_BUFSIZE;DWORDdw

18、EventTotal = 0,/ 程序中事件的总数dwRecvBytes = 0,/ 接收到的字符长度Flags = 0;/ WSARecv的参数【第二步】创建一个套接字,开始在指定的端口上监听连接请求 和其他的SOCKET初始化全无二致,直接照搬即可,在此也不多费唇舌了,需要 注意的是为了一目了然,我去掉了错误处理,平常可不要这样啊,尽管这里出错 的几率比较小。WSADATA wsaData;WSAStartup(MAKEWORD(2,2),&wsaData);ListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 创建 TCP套接字S

19、OCKADDR_IN ServerAddr;分配端口及协议族并绑定ServerAddr.sin_family=AF_INET;ServerAddr.sin_addr.S_un.S_addr =htonl(INADDR_ANY);ServerAddr.sin_port=htons(11111);bind(ListenSocket,(LPSOCKADDR)&ServerAddr, sizeof(ServerAddr); / 绑 定套接字listen(ListenSocket, 5);开始监听【第三步】接受一个入站的连接请求AcceptSocket = accept (ListenSocket, N

20、ULL,NULL);当然,这里是我偷懒,如果想要获得连入客户端的信息(记得论坛上也常有人问 到),accept的后两个参数就不要用NULL,而是这样SOCKADDR_IN ClientAddr;/ 定义一个客户端得地址结构作为参数 int addr_length二sizeof(ClientAddr);AcceptSocket = accept(ListenSocket,(SOCKADDR*)&ClientAddr, &addr_length);/于是乎,我们就可以轻松得知连入客户端的信息了LPCTSTR lpIP = inet_ntoa(ClientAddr.sin_addr);/ IPUIN

21、T nPort = ClientAddr.sin_port;/ Port【第四步】建立并初始化重叠结构为连入的这个套接字新建立一个WSAOVERLAPPED重叠结构,并且象前面讲到的那 样,为这个重叠结构从事件句柄数组里挑出一个空闲的对象句柄“绑定”上去。/创建一个事件/ dwEventTotal可以暂时先作为Event数组的索引EventArraydwEventTotal = WSACreateEvent();ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED);/ 置今AcceptOverlapped.hEvent = EventArray

22、dwEventTotal;/关联事件char bufferDATA_BUFSIZE;ZeroMemory(buffer, DATA_BUFSIZE);DataBuf.len = DATA_BUFSIZE;DataBuf.buf = buffer;/初始化一个WSABUF结构dwEventTotal +;总数加一【第五步】以WSAOVERLAPPED结构为参数,在套接字上投递WSARecv请求各个变量都已经初始化OK以后,我们就可以开始Socket操作了,然后让 WSAOVERLAPPED结构来替我们管理I/O请求,我们只用等待事件的触发就OK 了。if(WSARecv(AcceptSocket

23、 ,&DataBuf,1,&dwRecvBytes,&Flags,& AcceptOverlapped, NULL) = SOCKET_ERROR)(/返回WSA_IO_PENDING是正常情况,表示IO操作正在进行,不能立即完 成/如果不是WSA_IO_PENDING错误,就大事不好了! !if(WSAGetLastError() != WSA_IO_PENDING) (/那就只能关闭大吉了closesocket(AcceptSocket);WSACloseEvent(EventArraydwEventTotal); 【第六步】 用WSAWaitForMultipleEvents函数等待重叠

24、操作返回的结果我们前面已经给WSARecv关联的重叠结构赋了一个事件对象句柄,所以我们 这里要等待事件对象的触发与之配合,而且需要根据WSAWaitForMultipleEvents函数的返回值来确定究竟事件数组中的哪一个事件 被触发了,这个函数的用法及返回值请参考前面的基础知识部分。DWORD dwIndex;/等候重叠I/O调用结束/因为我们把事件和Overlapped绑定在一起,重叠操作完成后我们会接到事件 通知dwIndex = WSAWaitForMultipleEvents(dwEventTotal,EventArray ,FALSE ,WSA_INFINITE,FALSE);/注

25、意这里返回的Index并非是事件在数组里的Index,而是需要减去WSA_WAIT_EVENT_0dwIndex = dwIndex - WSA_WAIT_EVENT_0;【第七步】使用WSAResetEvent函数重设当前这个用完的事件对象事件已经被触发了之后,它对于我们来说已经没有利用价值了,所以要将它重置 一下留待下一次使用,很简单,就一步,连返回值都不用考虑WSAResetEvent(EventArraydwIndex);【第八步】使用WSAGetOverlappedResult函数取得重叠调用的返回状态这是我们最关心的事情,费了那么大劲投递的这个重叠操作究竟是个什么结 果呢?其实对于

26、本模型来说,唯一需要检查一下的就是对方的Socket连接是否 已经关闭了DWORD dwBytesTransferred;WSAGetOverlappedResult( AcceptSocket, AcceptOverlapped , &dwBytesTransferred, FALSE, &Flags);/先检查通信对方是否已经关闭连接/如果=0则表示连接已经,则关闭套接字if(dwBytesTransferred = 0)(closesocket(AcceptSocket);WSACloseEvent(EventArraydwIndex); / 关闭事件 return;【第九步】“享受”接

27、收到的数据如果程序执行到了这里,那么就说明一切正常,WSABUF结构里面就存有我们 WSARecv来的数据了,终于到了尽情享用成果的时候了!喝杯茶,休息一下吧DataBuf.buf就是一个char*字符串指针,听凭你的处理吧,我就不多说了【第十步】同第五步一样,在套接字上继续投递WSARecv请求,重复步骤69这样一路作下来,我们终于可以从客户端接收到数据了,但是回想起来,呀,这样岂不是只能收到一次数据,然后程序不就Over 了?.-_-b 所 以我们接下来不得不重复一遍第四步和第五步的工作,再次在这个套接字上投递 另一个WSARecv请求,并且使整个过程循环起来,are u clear?大家

28、可以参考我的代码,在这里就先不写了,因为各位都一定比我 smart,领悟了关键所在以后,稍作思考就可以灵活变通了。客户端情况的注意事项完成了上面的循环以后,重叠模型就已经基本上搭建好了 80%了,为 什么不是100%呢?因为仔细一回想起来,呀,这样岂不是只能连接一个客户端?是的,如果只处理一个客户端,那重叠模型就半点优势也没有了,我 们正是要使用重叠模型来处理多个客户端。所以我们不得不再对结构作一些改动。首先,肯定是需要一个SOCKET数组,分别用来和每一个SOCKET通信其次,因为重叠模型中每一个SOCKET操作都是要“绑定”一个重叠结构的,所 以需要为每一个SOCKET操作搭配一个WSAO

29、VERLAPPED结构,但是这样说并不严 格,因为如果每一个SOCKET同时只有一个操作,比如WSARecv,那么一个SOCKET 就可以对应一个WSAOVERLAPPED结构,但是如果一个SOCKET上会有WSARecv和 WSASend两个操作,那么一个SOCKET肯定就要对应两个WSAOVERLAPPED结构, 所以有多少个SOCKET操作就会有多少个WSAOVERLAPPED结构。然后,同样是为每一个WSAOVERLAPPED结构都要搭配一个WSAEVENT事件,所以 说有多少个SOCKET操作就应该有多少个WSAOVERLAPPED结构,有多少个 WSAOVERLAPPED结构就应该有多少个WSAEVENT事件,最好把SOCKET - WSAOVERLAPPED - WSAEVENT三者的关联起来,到了关键时刻才会临危不乱:)不得不分作两个线程:一个用来循环监听端口,接收请求的连接,然后给在这个套接字上配合一个 WSAOVERLAPPED结构投递第一个WSARecv请求,然后进入第二个线程中等待操作 完成。第二个线程用来不

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论