版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、期末大作业,聊天类程序。 语言:限C、C+ 、Java 提交材料: 设计方案(纸介质); 类图(纸介质) ; 运行界面截图(纸介质) ; 源码中设计socket编程部分的详细注释(纸介质); 5个以上错误调试的截图、解决办法说明(纸介质); 源代码(电子版,发至socket_,文件名为姓名学号) 纸介质中的每项占15分,源代码15分,平时10分。,2,第5章 WinSock的多线程编程及I/O模型,WinSock需要多线程编程的原因, Win32操作系统下的多进程多线程机制、多线程机制在网络编程中的应用 五种非阻塞工作模式 下的“套接字I/O模型”,3,5.1 WinSock为什么需要多线程编
2、程,5.1.1 WinSock的两种I/O模式 “阻塞”模式,又称为同步模式,执行I/O操作完成前会一直进行等待,不会将控制权交给程序,工作在“阻塞”模式的套接字称为阻塞套接字。 套接字 默认为阻塞模式。 可以通过多线程技术进行处理。 “非阻塞”模式,又称为异步模式,执行I/O操作时,Winsock函数会返回并交出控制权。工作在“非阻塞”模式下的套接字称为非阻塞套接字。 使用 起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回WSAEWOULDBLOCK错误,但功能强大。,4,在大多数情况下,非阻塞模式调用都会失败,返回一个WSAEWOULDBLOCK错误,表示操作的条件尚不具备,但
3、又不允许等待完成请求的操作。 非阻塞模式下会频繁返回错误,应仔细检查返回代码;并且在不成功的情况下 不应反复轮询.,5,5.1.2 两种模式的优缺点及解决方法 “阻塞”与“非阻塞”模式各有其优点和缺点。 阻塞套接字,I/O操作工作情况比较确定,无非是调用、等待、返回,大部分情况下I/O操作都能成功地完成,不过就是花费了等待的时间。 因而比较容易使用,容易编程; 但在应付诸如需要建立多个套接字连接来为多个客户服务的时候,或在数据的收发量不均匀的时候,或在输入输出的时间不确定的时候,却显得性能低下,甚至无能为力。,6,非阻塞套接字,需要编写更多代码,因为必须恰当地把握调用I/O函数的时机,尽量减少
4、无功而返的调用,还必须详加分析每个Winsock调用中收到的WSAEWOULDBLOCK错误,采取相应的对策. 这种I/O操作的随机性使得非阻塞套接字显得难于操作。 必须采取适当的对策,克服这两种模式的缺点,让阻塞和非阻塞套接字能够满足各种场合的要求. 对于阻塞的套接字工作模式,则进一步引入了多线程机制。 对于非阻塞的套接字工作模式,进一步引入了五种“套接字I/O模型”。,7,5.2 Win32操作系统下的 多进程多线程机制,5.2.1 Win32是单用户多任务的操作系统 微软最早的DOS是单用户单任务的。 后来的图形用户界面操作系统Windows,发展到Windows 95、Windows
5、98,就都支持多任务了; 从Windows NT起,Windows发展成了一个真正的抢占式多任务操作系统。 一个运行中的应用进程实例,就是一个进程。 一个基于Win32的应用程序可以包含一个或多个进程。,5.2.2 Win32 OS是支持多线程的操作系统 Win32操作系统还支持同一进程的多线程,在一个Windows进程内,可以包含多个线程。 一个线程(thread)是进程内的一条执行路径,具体地说,是一个应用程序中的一条可执行路径,往往是应用程序中的一个或多个函数。 一个进程中至少要有一个线程,习惯将它称为主线程。 任何一个应用程序进程都有一个主线程,一般C程序中的Main或WinMain函
6、数就规定了主线程的执行代码。,当你启动了一个应用程序时,操作系统在为它创建了进程之后,也创建了该进程的主线程,并根据Main或WinMain函数的地址,开始执行该进程的主线程。 主线程可以创建并启动其他辅助线程; 由主线程创建的辅助线程又可以创建并启动更多的线程。 线程的代码执行完毕时会自动终止,并将占用的资源释放给进程; 进程的所有线程都终止时,进程也就终止了,并会将占用的资源释放给操作系统。,10,5.2.3 多线程机制在网络编程中的应用 如果一个应用程序,有多个任务需要同时进行处理,那就最适合使用多线程机制。 对于网络上客户机软件,采用多线程的编程技术,能克服在单线程的编程模式下,由于阻
7、塞等待而产生的客户程序就不能及时响应用户的操作命令的问题,程序的界面就表现为类似死机的状态。 利用Windows系统的多线程机制可以很好的解决这个问题。 将用户界面的处理放在主线程中,将数据的I/O、费时的计算、网络访问等放在辅助线程里。,11,网络上服务器软件应采用多线程的编程技术。 能更好地为多个客户服务。 可以执行许多后台处理,如数据库访问、安全验证、日志记录、事物处理等。 客户机软件,采用多线程机制也能大大提高应用程序的运行效率。 如,东方快车、网络蚂蚁等文件下载软件,就采用了多线程机制,用多个线程同时下载一个文件的不同部分,大大加快了下载速度。 总之,多线程机制在网络编程中是大有作为
8、的。,12,5.3 Winsock的输入/输出模型,WinSock的I/O可以采用阻塞模式或非阻塞模式。 在非阻塞模式下,由于I/O操作的随机性,使非阻塞套接字难于操作,给编程带来困难。 为解决这个问题,对于非阻塞的套接字工作模式,进一步引入了五种“套接字I/O模型”,有助于应用程序通过一种异步方式,同时对一个或多个套接字上进行的通信加以管理。,13,这些模型包括: select(选择) WSAAsyncSelect(异步选择) WSAEventSelect(事件选择) Overlapped I/O(重叠式I/O) Completion port(完成端口) 不同的Windows平台支持不同的
9、I/O模型。,14,5.4 Select模型,Select(选择)模型是Winsock中最常见的I/O模型。 基本思想:是利用select函数,实现对多个套接字I/O的管理。 利用select函数,可以判断一个或多个套接字的状态,某个套接字是否存在要读取的数据,或者能否向一个套接字写入数据;只有在条件满足时,才对套接字进行输入输出操作,从而避免无功而返的I/O函数调用,避免频繁产生WSAEWOULDBLOCK错误,使I/O变得有序。,15,1 Select的函数 select的函数原型如下 其中fd_set数据类型,代表一系列特定套接字集合 int select( int nfds, /为保持
10、兼容而设,可忽略 fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout /用于决定Select等待I/O操作完成的最长时间,若为空指针,则一直等待,直到至少有一个套接字符合条件。 );,16,fd_set结构: #define FD_SETSIZE 64; typedef struct fd_set u_int fd_count; /* how many are SET? */ SOCKET fd_arrayFD_SETSIZE; /* an
11、 array of SOCKETs */ fd_set; fd_count为已设定socket的数量; fd_array为socket列表; FD_SETSIZE为最大socket数量,建议不小于64.,17,timeval结构: struct timeval long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ ; tv_sec为时间的秒值。 tv_usec为时间的毫秒值。 这个结构主要是设置select()函数的等待值,如果将该结构设置为(0,0),则select()函数 会立即返回。,18,2操作套接字集合的宏 在
12、应用程序中,用select对套接字进行监视之前,必须先将要检查的套接字句柄分配给某个集合,设置好相应的fd_set结构,再调用select函数。 Winsock提供了4个宏操作,专门对fd_set数据类型进行操作 (1)FD_CLR(s, *set):从set中删除套接字s。 (2)FD_ISSET(s, *set):检查s是否set集合的一名成员;如果是,则返回TRUE。 (3)FD_SET(s, *set):将套接字s加入集合set。 (4)FD_ZERO ( *set):将set初始化成空集合。 其中,参数s是一个要检查的套接字,参数set是一个fd_set集合类型的指针。,19,例如,
13、调用select函数前,可使用FD_SET宏,将指定的套接字加入到fd_read集合中,select函数完成后,可使用FD_ISSET宏,来检查该套接字是否仍在fd_read集合中。 3select模型的操作步骤 用select模型操作一个或多个套接字句柄,一般采用下述步骤: 使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set集合。 使用FD_SET宏,将要检查的套接字句柄添加到自己感兴趣的每个fd_set集合中,相当在指定的fd_set集合中,设置好要检查的I/O活动。,20,调用select函数,然后等待。select完成返回后,会修改每个fd_set结构,删除那些不存在待决I/O
14、操作的套接字句柄,在各个fd_set集合中返回符合条件的套接字。 根据select的返回值,使用FD_ISSET宏,对每个fd_set集合进行检查,判断一个特定的套接字是否仍在集合中,便可判断出哪些套接字存在尚未完成(待决)的I/O操作。 知道了每个集合中“待决”的I/O操作之后,对相应的套接字的I/O进行处理,然后返回步骤1,继续进行select处理。,21,5.5 WSAAsyncSelect异步I/O模型,异步I/O模型通过调用WSAAsyncSelect()函数实现. 利用这个模型,应用程序可在一个套接字上,接收以Windows消息为基础的网络事件通知。 该模型的实现方法是通过调用WS
15、AAsynSelect函数自动将套接字设置为非阻塞模式,并向WINDOWS注册一个或多个网络事件,并提供一个通知时使用的窗口句柄。 当注册的事件发生时,对应的窗口将收到一个基于消息的通知。 该模型最早出现于Winsock的1.1中,以适应其多任务消息环境。,22,1WSAAsyncSelect函数 函数的定义是: int WSAAsyncSelect( SOCKET s, /s为需要事件通知的套接字 HWND hWnd, unsigned int wMsg, long lEvent ); hWnd为接收消息的窗口句柄 wMsg为要接收的消息 lEvent为掩码,指定应用程序感兴趣的网络事件组合
16、。,23,2窗口回调例程 应用程序在一个套接字上调用WSAAsyncSelect函数时,该函数的hWnd参数指定了一个窗口句柄。 函数成功调用后,当指定的网络事件发生时,会自动执行该窗口对应的窗口回调例程。 并将网络事件通知和Windows消息的相关信息,传递给该例程的入口参数,用户可以在该例程中添加自己的代码,针对不同的网络事件进行处理,从而实现有序的套接字输入和输出。,24,窗口回调例程应定义成如下形式: LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );,5.6 WSAEve
17、ntSelect事件选择模型,WSAEventSelect事件选择模型和WSAAsyncSelect模型类似,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。 该模型最主要的差别在于,网络事件会投递至一个事件对象句柄,而非投递至一个窗口例程。 表5.1总结的、由WSAAsyncSelect模型采用的网络事件,均可原封不动地移植到事件选择模型中。,26,1创建事件对象句柄 事件选择模型要求应用程序针对每一个套接字,首先创建一个事件对象。 创建方法是调用WSACreateEvent函数,它的定义如下: WSAEVENT WSACreateEvent(void); 函数的返回值
18、很简单,就是一个创建好的事件对象句柄。,27,2关联套接字和事件对象,注册关心的网络事件 有了事件对象句柄后,接下来必须将其与某个套接字关联在一起,同时注册感兴趣的网络事件类型(表5.1),需要调用WSAEventSelect函数,函数的定义为: int WSAEventSelect( SOCKET s, /感兴趣的套接字 WSAEVENT hEventObject, /与套接字关联在一起的事件对象 long lNetworkEvents /指定应用程序感兴趣的各种网络事件类型组合。 );,表5.1 用于WSAAsyncSelect函数的网络事件类型,29,3. 等待网络事件触发事件对象句柄的
19、工作状态 将一个套接字同一个事件对象句柄关联在一起以后,应用程序便可以调用WSAWaitForMultipleEvents函数,等待网络事件触发事件对象句柄的工作状态。 该函数用来等待一个或多个事件对象句柄,当其中一个或所有句柄进入“已传信”状态后,或在超过了一个规定的时间期限后,立即返回。 该函数的定义: DWORD WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT FAR * lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable );,30,4检查套接字上所发生的网
20、络事件类型 确定了造成网络事件的套接字后,接下来可调用WSAEnumNetworkEvents函数,检查套接字上发生了什么类型的网络事件。 该函数定义如下: int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );,31,5处理网络事件 在确定了套接字上发生的网络事件类型后,可以根据不同的情况做出相应的处理。 完成了对WSANETWORKEVENTS结构中的事件的处理之后,应用程序应在所有可用的套接字上,继续等待更多的网络事件。 应用程序完成了对一个事件对象的
21、处理后,便应调用WSACloseEvent函数,释放事件句柄使用的系统资源. 函数的定义如下: BOOL WSACloseEvent(WSAEVENT hEvent); 该函数也将一个事件句柄作为自己唯一的参数,并会在成功后返回TRUE,失败后返回FALSE。,5.7 其它模型,重叠(Overlapped )I/O模型 能使应用程序达到更佳的性能。 基本原理:应用程序使用一个重叠的数据结构,一次投递一个或多个Winsock的I/O请求,应用程序可为那些提交的请求提供服务。 自Winsock 2.0发布开始,重叠I/O便已集成到新的Winsock函数中,如WSASend和WSARecv等,适用于
22、安装了Winsock 2.0的所有Windows平台。 在一个套接字上使用重叠I/O模型,首先,必须使用WSA_FLAG_OVERLAPPED标志创建一个套接字。 s=WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);,33,使用socket函数, 而非WSASocket函数创建套接字时,会默认设置WSA_FLAG_OVERLAPPED标志。 建好一个套接字并将其绑定到一个本地接口后,便可调用下述的Winsock 函数开始进行重叠I/O操作,同时指定一个WSAOVERLAPPED结构,函数依赖于WSAOVERLA
23、PPED结构来返回一个I/O请求的返回. WSASend、WSASendTo、WSARecv、WSARecvFrom、WSALoctl、AcceptEx、TrnasmitFile 以上每个函数都与一个套接字上数据的发送、数据接收以及连接的接受有关,极少的时间内就能完成。 若随一个WSAOVERLAPPED结构一起调用,函数会立即完成并返回,无论套接字是否设为锁定模式。,34,有两个方法可用来管理一个重叠I/O请求: 令应用程序等待“事件对象通知”; 通过“完成例程”对已经完成的请求加以处理. 前述函数(除AcceptEx外)还有一个常用参数: lpCompletionROUTINE 该参数指定
24、一个可选的指针,指向一个完成例程函数,在重叠请求完成后调用。 1. 事件通知 重叠I/O模型的事件通知方法要求将Win32事件对象与WSAOVERLAPPED结构关联在一起。 若使用一个WSAOVERLAPPED结构,发出WSASend和WSARecv这样的I/O调用,会立即返回。,35,这些I/O调用通常以失败告终,返回SOCKET_ERROR,这个错误状态意味着I/O操作正在进行,使用WSAGetLastError函数,可获得与错误状态有关的一个报告。 稍后,应用程序需要等候与WSAOVERLAPPED结构对应的事件对象,获知一个重叠I/O请求何时完成。 WSAOVERLAPPED结构在一
25、个重叠I/O请求的初始化及其后续完成之间,提供了一种沟通或通信机制。 下面是这个结构的定义: typedef struct WSAOVERLAPPED DWORD Internal; DWORD InternalHigh; DWORD Offset; DWORD OffsetHigh; WSAEVENT hEvent; WSAOVERLAPPED Far * LPWSAOVERLAPPED,36,在这个结构中: Internal、InternalHigh、Offset和OffsetHigh字段由系统在内部使用,不应由应用程序直接处理或使用; hEventh 字段允许应用程序将一个事件对象句柄同
26、一个套接字关联起来,可用WSACreatEvent函数来创建一个事件对象句柄,并将重叠结构的hEventh字段分配给事件句柄,再使用重叠结构,调用一个Winsock函数即可,如WSASend或WSARecv。,37,一个重叠I/O请求最终完成后,应用程序要负责取回重叠I/O操作的结果。 在事件通知方法中, Winsock会更改与一个WSAOVERLAPPED结构对应的一个事件对象的事件传信状态,将其从“未传信”变成“已传信”。 然后,调用WSAWaitForMultipleEvents函数,判断一个重叠I/O调用在什么时候完成。 WSAWaitForMultipleEvents函数会等候一段规
27、定的时间,等待一个或多个事件对象进入“已传信”状态,该函数一次最多只能等待64个事件对象。,38,获知一次重叠请求完成之后,接着需要调用WSAGetOverlappedResult(取得重叠结构)函数,判断重叠调用到底是成功,还是失败。 该函数定义如下: BOOL WSAGetOverlappedResult ( SOCKET s; LPWSAOVERLAPPED lpoverlapped; LPDWORD lpcbTransfer; BOOL fWait; LPDWORD lpdwFlags ) ;,39,s:用于指定在重叠操作开始时与之对应的套接字。 lpOverlapped:是一个指针,
28、对应于在重叠操作开始时,指定的那个WSAOVERLAPPED结构。 lpcbTransfer:也是一个指针,对应一个DWORD(变量,负责接收一次重叠发送或接收操作实际传输的字节数。 fWait:用于决定函数是否应该等待一次待决(未决)的重叠操作完成。 若设为TRUE,则除非操作完成,否则函数不会返回; 若设为FALSE,而且操作仍然处于“待决”状态,那么WSAGetOverlappedResult函数会返回FALSE值,同时返回一个WSA_IO_INCOMPLETE (I/O操作未完成)错误。 但就我们目前的情况来说,由于需要等候重叠操作的一个已传信事件完成,所以该参数无论采用什么设置,都没
29、有任何效果。 lpdwFlags :对应于一个指针,指向一个DWORD,负责接收结果标志(假如原先的重叠调用是用WSARecv或WSARecv From函数发出的)。,40,若WSAGetOverlappedResult函数调用成功,返回值是TRUE ,意味着重叠I/O操作已成功完成,而且由lpcbTransfer参数指向的值已进行了更新。 若返回值是FALSE,那么可能是由下述任何一种原因造成的: 重叠I/O操作仍处在“待决”状态; 重叠操作已经完成,但含有错误; 重叠操作的完成状态不可判决,因为在提供给WSAGetOverlappedResult函数的一个或多个参数中存在着错误。 失败后,
30、由lpcbTransfer参数指向的值不会进行更新,并且应用程序应调用WSAGetLastError函数,调查到底是何种原因造成了调用失败。,41,在下面的程序清单中,阐述如何编制一个简单的服务器应用,令其在一个套接字上对重叠I/O操作进行管理,程序完全利用了前述的事件通知机制。 对该程序采用的编程步骤总结如下: 1) 创建一个套接字,开始在指定端口上监听连接请求. 2) 接受一个进入的连接请求。 3) 为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄,同时将事件对象句柄分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents函数使用。
31、 4) 在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。注意函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDING(I/O操作尚未完成)。,42,5) 使用步骤3 )的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。 6) WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。 7) 使用WSAGetOverl
32、appedRESULT函数,判断重叠调用的返回状态是什么。 8) 在套接字上投递另一个重叠WSARecv请求。 9) 重复步骤5 ) 8 )。 这个例子极易扩展,提供对多个套接字的支持,方法是将代码的重叠I/O处理部分移至一个独立的线程内,让主应用程序线程为附加的连接请求提供服务。,43,2. 完成例程 应用程序用来管理完成的重叠I/O请求的另一种方法. 实质是一些函数,开始时将其传递给一个重叠I/O请求,继而在一个重叠I/O请求完成时由系统调用。 基本设计宗旨,是通过调用者的线程,为一个已完成的I/O请求提供服务。 除此以外,应用程序可通过完成例程,继续进行重叠I/O处理。 如果希望用完成例
33、程为重叠I/O请求提供服务,在应用程序中必须为一个I/O定界Winsock函数,指定一个完成例程,同时指定一个WSAOVERLAPPED结构。,44,一个完成例程必须拥有下述函数原型: void CALLBACK CompletionROUTINE ( DWORD dwError; DWORD cbTransferred; LPWSAOVERLAPPED lpOverlapped; DWORD dwFlags); 用完成例程完成了一个重叠I/O请求之后,参数中会包含下述信息: dwError参数表明了一个重叠操作(由lpOverlapped指定)的完成状态是什么。 cbTransferred参
34、数指定了在重叠操作期间,实际传输的字节量是多大。 lpOverlapped参数指定的是传递到最初的I/O调用内的一个WSAOVERLAPPED结构。 dwFlags参数目前尚未使用,应设为0。,45,在用一个完成例程提交的重叠请求,与用一个事件对象提交的重叠请求之间,存在一项非常重要的区别: WSAOVERLAPPED结构的hEvent字段并未使用,即不可将一个事件对象同重叠请求关联到一起。 用完成例程发出一个重叠I/O调用之后,一旦完成调用线程必须为完成例程提供服务,于是便要求将调用线程置于一种“可警告的等待状态”,并在I/O操作完成后,对完成例程加以处理。 WSAWaitForMultip
35、leEvents函数可用来将线程置于一种可警告的等待状态。 这样做的缺点在于,还必须有一个事件对象可用于WSAWaitForMultipleEvents函数。,46,假定应用程序只用完成例程对重叠请求进行处理,便不可能有任何事件对象需要处理。 作为一种变通方法,应用程序可用Win32的SleepEx函数将自己的线程置为一种可警告等待状态。 当然,亦可创建一个伪事件对象,不将它与任何东西关联在一起。 假如调用线程经常处于繁忙状态,而且并不处在一种可警告的等待状态,那么根本不会有投递的完成例程得到调用。 WSAWaitForMultipleEvents通常会等待同WSAOVERLAPPED结构关联
36、在一起的事件对象。,47,该函数也设计用于将线程置入一种可警告等待状态,并可为已经完成的重叠I/O请求进行完成例程的处理(前提是将fAlertable 参数设为TRUE)。用一个完成例程结束了重叠I/O请求之后,返回值是WSA_IO_COMPLETION,而不是事件数组中的一个事件对象索引。,48,SleepEx函数的行为和WSAWaitForMultipleEvents差不多,只是不需要任何事件对象。 对SleepEx函数的定义如下: DWORD SleepEx ( DWORD dwMilliseconds; BOOL bAlertable ) ; dwMilliseconds参数,定义了S
37、leepEx函数的等待时间,以毫秒为单位。 假如将dwMilliseconds设为INFINITE,那么SleepEx会无休止地等待下去。 bAlertable参数,规定了一个完成例程的执行方式。 如将bAlertable设为FALSE,而且进行了一次I/O完成回调,那么I/O完成函数不会执行,而且函数不会返回,除非超过由dwMilliseconds规定的时间。 若设为TRUE,那么完成例程会得到执行,同时SleepEx函数返回WAIT_IO_COMPLETION。,49,在下面的程序清单中,阐述如何构建一个简单的服务器应用,令其采用前述的方法,通过完成例程,来实现对一个套接字请求的管理。 对
38、该程序采用的编程步骤总结如下: 1) 新建一个套接字,开始在指定端口上,监听一个进入的连接。 2) 接受一个进入的连接请求。 3) 为接受的套接字创建一个WSAOVERLAPPED结构 4) 在套接字上投递一个异步WSARecv请求,方法是将WSAOVERLAPPED指定成为参数,同时提供一个完成例程。,50,5) 在将fAlertable参数设为TRUE的前提下,调用WSAWaitForMultipleEvents,并等待一个重叠I/O请求完成。重叠请求完成后,完成例程会自动执行,而且WSAWaitForMultipleEvents会返回一个WSA_IO_COMPLETION。在完成例程内,
39、可随一个完成例程一道,投递另一个重叠W S A R e c v请求。 6) 检查WSAWaitForMultipleEvents是否返回WSA_IO_COMPLETION 。 7) 重复步骤5 )和6 )。,“完成端口”模型 是迄今为止最为复杂的一种I/O模型。 只适用于Windows NT和Windows 2000操作系统 假若一个应用程序需要同时管理为数众多的套接字,那么采用这种模型往往可以达到最佳的系统性能! 因其设计的复杂性,只有在应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升时,才应考虑采用“完成端口”模型。,5
40、2,完成端口模型,要求创建一个Win32完成端口对象,通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。 使用这种模型,首先要调用CreateIoCompletionPort函数创建一个I/O完成端口对象,用它面向任意数量的套接字句柄,管理多个I/O请求。 该函数定义如下: HANDLE CreateIoCompletionPort ( HANDLE FileHandle; HANDLE ExistingCompletionPort; DWORD CompletionKey; DWORD NumberOfCurrentThread ) ; 可以看出,该函数实
41、际用于两个明显有别的目的: 用于创建一个完成端口对象。 将一个句柄同完成端口关联到一起。,53,创建一个完成端口时,前三个参数一般都会被忽略,需要注意的是NumberOfCurrentThread参数,定义了在一个完成端口上同时允许执行的线程数量(并发线程的数量)。 理想情况下,希望每个处理器各自负责一个线程的运行,为完成端口提供服务,避免过于频繁的线程“场景”切换。 若将该参数设为0,表明系统内安装了多少个处理器,便允许同时运行多少个线程! 例如,创建一个I/O完成端口: CompletionPort=CreatIoCompletionPort(INVALID_HANDLE_VALUE, N
42、ULL, 0, 0); 作用是返回一个句柄,在为完成端口分配了一个套接字句柄后,用来对那个端口进行标定(引用)。,54,1. 工作者线程与完成端口 在关联套接字之前,首先必须创建一个或多个“工作者线程”,以便在I/O请求投递给完成端口对象后,为完成端口提供服务。 服务I/O请求所需的数量取决于应用程序的总体设计情况,在调用CreatIoCompletionPort时指定的并发线程数量,与打算创建的工作者线程数量相比,它们代表的并非同一件事情。 CreatIoCompletionPort函数的参数NumberOfCurrentThreads明确指示系统:在一个完成端口上,一次只允许n个工作者线程
43、运行。假如在完成端口上创建的工作者线程数量超出n个,那么在同一时刻,最多只允许n个线程运行。 在一段较短的时间内,系统有可能超过这个值,但很快便会把它减少至事先在CreatIoCompletionPort函数中设定的值。,55,为何实际创建的工作者线程数量有时要比CreatIoCompletionPort函数设定的多一些呢? 这主要取决于应用程序的总体设计情况。 假定某个工作者线程调用了一个函数,如Sleep或WaitForSingleObject,但却进入了暂停(锁定或挂起)状态,那么应该允许另一个线程代替它的位置,即希望随时都能执行尽可能多的线程;而最大线程数量是事先在CreatIoCom
44、pletionPort调用里设定好的。 因此,假如事先预计到自己的线程有可能暂时处于停顿状态,那么最好创建比CreatIoCompletionPort的NumberOfCurrentThreads参数的值多的线程,以便到时候充分发挥系统的潜力。,56,一旦在完成端口上拥有足够多的工作者线程来为I / O请求提供服务,便可着手将套接字句柄同完成端口关联到一起! 在一个现有的完成端口上调用CreatIoCompletionPort函数,为前三个FileHandle、ExistingCompletionPort和CompletionKey参数提供套接字的信息。 FileHandle参数,指定一个要同
45、完成端口关联在一起的套接字句柄。 ExistingCompletionPort参数,指定的是一个现有的完成端口。 CompletionKey(完成键)参数,则指定要与某个特定套接字句柄关联在一起的“单句柄数据”。,57,在CompletionKey(完成键)参数中,应用程序可保存与一个套接字对应的任意类型的信息。 之所以叫作“单句柄数据”,是由于它只对应着与那个套接字句柄关联在一起的数据。 可将其作为指向一个数据结构的指针,来保存套接字句柄;在那个结构中,同时包含了套接字的句柄,以及与那个套接字有关的其他信息。 为完成端口提供服务的线程例程可通过这个参数,取得与套接字句柄有关的信息。,58,在
46、下面的程序清单中,阐述如何使用完成端口模型开发一个回应(或“反射”)服务器应用。 对该程序采用的编程步骤总结如下: 1) 创建一个完成端口。第四个参数设为0,指定在完成端口上每个处理器一次只允许执行一个工作者线程。 2) 判断系统内到底安装了多少个处理器。 3) 创建工作者线程,根据步骤2)得到的处理器信息,在完成端口上,为已完成的I/O请求提供服务。 在这个例子中,为每个处理器都只创建一个工作者线程。这是由于事先已预计到,到时不会有任何线程进入“挂起”状态,造成由于线程数量的不足,而使处理器空闲的局面(没有足够的线程可供执行)。调用CreateThread函数时,必须同时提供一个工作者例程,
47、由线程在创建好执行。 4) 准备好一个监听套接字,在端口5150上监听进入的连接请求。,59,5) 使用accept函数,接受进入的连接请求。 6) 创建一个数据结构,用于容纳“单句柄数据”,同时在结构中存入接受的套接字句柄。 7) 调用CreateIoCompletionPort ,将自accept返回的新套接字句柄同完成端口关联到一起。通过完成键( CompletionKey )参数,将单句柄数据结构传递给CreateIoCompletionPort 。 8) 开始在已接受的连接上进行I/O操作。通过重叠I/O机制,在新建的套接字上投递一个或多个异步WSARecv或WSASend请求。这些
48、I/O请求完成后,一个工作者线程会为I/O请求提供服务,同时继续处理未来的I/O请求。 9) 重复步骤5 ) 8 ),直至服务器中止。 这个例子极易扩展,提供对多个套接字的支持,方法是将代码的重叠I/O处理部分移至一个独立的线程内,让主应用程序线程为附加的连接请求提供服务。,60,2. 完成端口和重叠I/O 将套接字句柄与一个完成端口关联在一起后,便可以套接字句柄为基础,投递发送与接收请求,开始对I/O请求的处理。 接下来,可依赖完成端口,接收有关I/O操作完成情况的通知。 本质上,完成端口模型利用了Win32重叠I/O机制,像WSASend和WSARecv这样的Winsock API调用会立
49、即返回,此时,需要由应用程序负责在以后的某个时间,通过一个OVERLAPPED结构接收调用的结果。 可使用GetQueuedCompletionStatus (获取排队完成状态)函数,让一个或多个工作者线程在完成端口上等待。,61,GetQueuedCompletionStatus 函数定义如下: BOOL GetQueuedCompletionStatus ( HANDLE CompletionPort LPDWORD lpNumberOfBytesTransferred; LPDWORD lpCompletionKey; LPOVERLAPPED *lpOverlapped; DWORD
50、dwMilliseconds); 其中: CompletionPort参数,对应要在上面等待的完成端口。 lpNumberOfBytesTransferred参数,负责在完成了一次I/O操作后接收实际传输的字节数。 lpCompletionKey参数,为传递进CreateCompletionPort 函数的套接字返回“单句柄数据”,如前所述,最好将套接字句柄保存在这个“键”(K e y)中。,62,GetQueuedCompletionStatus 函数定义如下: BOOL GetQueuedCompletionStatus ( HANDLE CompletionPort LPDWORD lp
51、NumberOfBytesTransferred; LPDWORD lpCompletionKey; LPOVERLAPPED *lpOverlapped; DWORD dwMilliseconds); 其中: lpOverlapped参数用于接收完成的I / O操作的重叠结果,这实际是一个相当重要的参数,因为可用它获取每个I / O操作的数据。 dwMilliseconds参数,用于指定调用者希望等待一个完成数据包在完成端口上出现的时间。假如将其设为I N F I N I T E,调用会无休止地等待下去。,63,3. 单句柄数据和单I/O操作数据 一个工作者线程从GetQueuedCompl
52、etionStatus调用接收到I/O完成通知后,在参数 lpCompletionKey和lpOverlapped中,会包含一些必要的套接字信息。 利用这些信息,可通过完成端口继续在一个套接字上的I/O处理。 通过这些参数,可获得两方面重要的套接字数据:单句柄数据、单I/O操作数据。 lpCompletionKey,包含 “单句柄数据”,在一个套接字首次与完成端口关联到一起时,这些数据便与一个特定的套接字句柄对应起来了,它们是在进行CreateIoCompletionPort API调用时由CompletionKey参数传递的。,64,lpOverlapped参数,包含一个OVERLAPPED结构,其后跟随“单I/O操作数据”。 工作者线程处理一个完成数据包时(将数据原封不动打转回去,接受连接,投递另一个线程, 等等), “单I/O操作数据”信息是它必须要知道的。 单I/O
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 46026-2025家用和类似用途布艺清洁机
- 大秦医院面试题及答案
- C语言基础选择测试题含多知识点考察及答案
- 感控护士院感防控知识试题及答案
- 新疆成人考试真题及答案
- 成都三基试题题库附答案
- 市事业单位招聘考试公共基础知识试题题库附答案详解
- 输血三基考试试题及答案
- 三级医院护士招聘面试题含答案
- 嵌入式开发面试题及答案
- 起重设备安全使用指导方案
- 江苏省扬州市区2025-2026学年五年级上学期数学期末试题一(有答案)
- 干部履历表(中共中央组织部2015年制)
- GB/T 5657-2013离心泵技术条件(Ⅲ类)
- GB/T 3518-2008鳞片石墨
- GB/T 17622-2008带电作业用绝缘手套
- GB/T 1041-2008塑料压缩性能的测定
- 400份食物频率调查问卷F表
- 滑坡地质灾害治理施工
- 实验动物从业人员上岗证考试题库(含近年真题、典型题)
- 可口可乐-供应链管理
评论
0/150
提交评论