网络模型和完成端口_第1页
网络模型和完成端口_第2页
网络模型和完成端口_第3页
网络模型和完成端口_第4页
网络模型和完成端口_第5页
全文预览已结束

下载本文档

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

文档简介

1、网络模型和完成端口简单介绍下完成端口吧。废话不多说,先从简单的一点一点往深里说。首先,要理解SOCKET编程,熟悉windows编程的估计都知道socket(xxx) 创建一个socket,可以实现网络通讯,实际上,linux下一样是用socket函数创建套接字,简单说socket是一个标准,并非windows独有。当然windows也有自己的特色,比如WINDOWS下必须先调用 WSAStartup 而 linux 就不用。额,扯远了,再说这个,比如我要实现一个TCP的程序,则需要使用 send/recv 这两个函数,OK,这里就有个问题了,用过recv函数的人都知道,recv调用以后会停在

2、那里,这个叫做阻塞,只有当有数据(IO操作)的时候,recv才会返回。这个就是传说中的阻塞模式。显然,如果我们是一个UI程序(图形界面程序)我们总不会希望我们的程序变成白白的窗口,直到SOCKET返回时候才继续执行,为了解决这个问题,诞生了几种网络模型。先说最简单的一种,叫 select 模型,为什么叫这个名字,因为这个模型的核心函数就是 select 函数。 select模型有个核心的结构叫做typedef struct fd_set u_int fd_count; SOCKET fd_arrayFD_SETSIZE; fd_set;他的主要思想是将socket全部放入这个结构中,然后sel

3、ect等待若干秒,然后检查某个socket是否还在这个结构中,就知道这个socket是否发生事件(例如读事件)这样,一个线程循环检查就可以管理很多个socket实现一个简单的服务端,select模型虽然很简单,但是应用却很广泛。一般用在需要实现一个简单的服务端或客户端功能,但是又没必要使用比较复杂的模型的时候。当然,select也有一些致命的缺点,可以看到他管理套接字是用的一个数组,数组大小是 FD_SETSIZE ,在winsock2.h 109行定义了/* * Select uses arrays of SOCKETs. These macros manipulate such * arr

4、ays. FD_SETSIZE may be defined by the user before including * this file, but the default here should be >= 64. * * CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE * INCLUDED IN WINSOCK2.H EXACTLY AS SHOWN HERE. */#ifndef FD_SETSIZE#define FD_SETSIZE 64#endif /* FD_SETSIZE */也就是默认情况下最大

5、只能支持64个套接字,当然可以重定义,但是实际上,select 模型处理大量连接的时候就显得无能为力了。WSAAsyncSelect模型这个有些地方叫消息套接字模型,因为他的特点就是使用了windows消息机制,顾名思义,就是先要设置一个消息,和消息响应函数,然后当SOCKET有事件的时候,WINDOWS会发送消息到程序。很明显,这个模型不需要线程去轮询,缺点也很明显,只有GUI程序才能接收消息。下面就是比较常用和高级的几个模型了,首先是事件模型WSAEventSelect模型这个模型的原理就是设置一个事件对象,一般用 WSACreateEvent 创建,然后需要设置事件,我的IOCP类,主l

6、isten socket 就是使用这个模型来 accept 连接的。例如响应int nRet = WSAEventSelect(m_sListen, m_lsEvent, FD_ACCEPT); / 选择处理 FD_ACCEPT然后,就是等待事件对象。/ 等待事件 DWORD dwRet = WSAWaitForMultipleEvents(1, &pThis->m_lsEvent, FALSE, 1000, FALSE); if (WSA_WAIT_TIMEOUT = dwRet) continue;当然,如果没有事件的时候,wait 返回的是超时,当有事件发生的时候,下一步就

7、需要枚举事件/ 得到网络事件 dwRet = WSAEnumNetworkEvents(pThis->m_sListen, pThis->m_lsEvent, &events); if (SOCKET_ERROR = dwRet) continue; if (events.lNetworkEvents & FD_ACCEPT) if (events.iErrorCodeFD_ACCEPT_BIT = 0) pThis->OnAccept(); else continue; 现在判断到发生事件是FD_ACCEPT就可以调用 lpContext->sockA

8、ccpeted = WSAAccept(m_sListen, (LPSOCKADDR)&lpContext->clientAddr, &nSize, NULL, 0);这个模型效率很高的,处理成百乃至上千个连接应该都没有问题,但是这个模型还是有个效率的问题,因为如果是FD_READ ,FD_WRITE 这些消息,需要再次调用 recv 之类的函数去读取。那么有没有效率更高的网络模型呢,下面隆重介绍重叠IO模型重叠IO模型严格的说,重叠IO模型并不是网络模型,他用在很多地方,例如ReadFileEx函数BOOL WINAPI ReadFileEx( _in HANDLE h

9、File, _out LPVOID lpBuffer, _in DWORD nNumberOfBytesToRead, _in LPOVERLAPPED lpOverlapped, _in LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);他的核心结构就是 OVERLAPPED 他的原理和事件模型很像,也是需要通过事件去判断SOCKET发生的事件,但是有一点很大的区别,就是事件模型,首先去判断事件,然后等待事件发生,再用IO操作函数(例如读取函数recv)去读取缓冲区,而重叠IO不同,重叠IO将缓冲区交给系统来处理,例如同样是读取,重叠I

10、O先发送一个RECV操作,需要注意的是这个操作是异步的,也就是说只是投递了一个RECV请求,这个请求并不会阻塞线程,做过驱动的人都知道驱动里有个状态叫 IO_PENDING 也就是IO操作等待,未返回,重叠IO就是这样的,他投递一个RECV请求,然后等待请求返回,当请求返回的时候,实际上收到的信息已经在buffer里了,这个就是重叠IO模型。和事件模型对比下,事件模型的特点就是先等待,然后再IO操作,而重叠IO则是先IO操作,然后等待这个IO操作返回。重叠IO模型效率比事件模型要高出不少,另外就是重叠IO不止用在网络上,比如快速读写一个文件,很多时候也用到了重叠IO,异步的方式读写一个文件,效

11、率要比同步方式快。另外重叠IO还可以设置完成例程,这个自己Google。下面最后一个网络模型,也是究极BOSS。完成端口完成端口,又叫IOCP完成端口的原理其实和重叠IO差不多,因为上述模型都有个致命的缺点,假设有1000个客户连接,并且同时并发的有数千个请求,那么怎么处理这些请求效率最高呢,假设一个请求我要处理100MS,那么1000个请求,最后一个请求得到处理就要等100秒时间,显然这个不是我们所希望的,聪明的程序员想到了多线程,OK又有个问题,假设一个连接分配一个线程,1000个连接就是1000个线程,那么10000个呢,WINDOWS将会花费大量时间去进行线程上下文的切换。解决这一问题

12、的终极办法就是IOCP,学过数据结构的人肯定知道一个数据结构叫队列。IOCP将队列和重叠IO结合起来。首先IOCP会创建几个线程,并将这些线程处于阻塞状态,他们共同从队列读取消息,当然队列为空,所以线程阻塞。现在我们将SOCKET投递一个IO请求,例如投递一个RECV请求,和重叠IO一样,这个请求会一直处于PENDING状态,当某一时刻,这个IO请求返回了,也就是接受到消息了,IOCP会将这个事件加入队列,这个时候,我们的工作线程之一就会从队列得到IO操作的结果,并进行处理,由于工作线程不止一个,所以当多个IO请求同时并发的时候,也不会造成阻塞,当然也不是绝对,如果消息太多,处理时间又很长的话

13、,还是会造成请求的阻塞,但是这个时候这些消息会放在IOCP的队列中,当有线程处理完后会立即处理。因为IOCP和内核相关,所以速度很快,加上合理的队列化请求,使得这个模型成为所有网络模型中效率最高的模型,当然这个不是绝对。更高效的改进。我参见了gh0st的IOCP类,和多个服务器程序的IOCP类,以及百度google总结出来的一些算是经验吧。首先,使用了IOCP最多算你手握M82A1,但是有一个好的武器是不够的,你还要有好的外围设备。这里介绍一些池的概念,实际上IOCP用到了线程池。假设我们的程序,需要处理很多个连接,我们频繁的调用createthread,则系统会频繁的为我们准备线程环境,这个

14、也是很耗时间的,那么怎么办呢,方法就是线程池,我们先将线程全部申请好,当然这个数量不宜太多,并让线程处于等待状态,当我们有需要的时候,我们从线程池中抽取一个线程执行我们的任务,执行完成后再把线程扔回线程池。这个就是线程池。同理还有内存池,内存池效果也很明显,原理一样,我们先申请一些内存,避免频繁的申请内存,然后我们需要的时候,从内存池取出一块内存交给程序处理,处理完后又将内存回收,内核中也有个类似的结构,避免频繁的申请内存。这里举gh0st的代码,gh0st为每个连接分配了一个clientContext结构,但是有连接断开以后,它并没有释放这个结构,而是将它移到一个FreePool的链表里,当下一次有连接连入的时候,他先检查FreePool链表,如果链表空的,则还是老老实实申请内存,但是链表不为空,则从FreePool里抓一个ClientContext用,这

温馨提示

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

评论

0/150

提交评论