WINSOCK I\O模型有六种:
一:select模型
二:WSAAsyncSelect模型
三:WSAEventSelect模型
四:Overlapped I/O 事件通知模型
五:Overlapped I/O 完成例程模型
六:完成端口IOCP模型
且一个比一个完善,一个比一个高深。最好用的莫过于完成端口,但可惜的是只有NT、2000的系统才支持这种功能。心痛之余,我们只能寄希望于Overlapped I/O模型。
下面,我们来详细分析一下重叠模型:
一、重叠模型的优点
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% 左右 �D�D 非常好的性能,已经直逼完成端口了^_^
二、重叠模型的基本原理
说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提一下重叠模型的基本原理。
概括一点说,重叠模型是让应用程序使用重叠数据结构(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以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据了。
三、关于重叠模型的基础知识
下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。
1)WSAOVERLAPPED结构
这个结构自然是重叠模型里的核心,它是这么定义的
我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:
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多个连接就不行了,这时,我们只能用后面的完成例程或完成端口了,这也正是它的弊端所在。