你的位置:pcMing工作室 >> 资讯 >> 编程开发 >> C++编程 >> 详细内容 在线投稿

Winsock 完成端口模型简介

排行榜 收藏 打印 发给朋友 举报 来源: 互联网   发布者:未知
热度1217票  浏览420次 【共0条评论】【我要评论 时间:2010年4月08日 19:56
pcMing工作室BPzgM4q f&Y v

摘自《Networking Programming for Microsoft Windows》第八章

/uV$W-D/v7GnC?1Jx0pcMing工作室E'V$jp[5K

“完成端口”模型是迄今为止最为复杂的一种I/O模型。然而,假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!pcMing工作室(w{fB?:u0f U0U

pcMing工作室K:DEFDi4]1O w

从本质上说,完成端口模型要求我们创建一个Win32完成端口对象,通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。

5h8QV;{7|0

IdN.I1x!MZvO{0使用这种模型之前,首先要创建一个I/O完成端口对象,用它面向任意数量的套接字句柄,管理多个I/O请求。要做到这一点,需要调用CreateCompletionPort函数。pcMing工作室'a;^:smOS
该函数定义如下:pcMing工作室 p:b h"L l d f

A Ed~"FB0
HANDLE CreateIoCompletionPort(
PB `9z?`_*~ XUI0    HANDLE FileHandle,pcMing工作室s4U%ra-j/v J)[
    HANDLE ExistingCompletionPort,pcMing工作室(q&upx,r3Lk
    ULONG_PTR CompletionKey,
ov6J a0J,P:U,~H4e0    DWORD NumberOfConcurrentThreadspcMing工作室^4\o%tv:d8{'],T
);
pcMing工作室@7nQ9]$uW5t`6\
在我们深入探讨其中的各个参数之前,首先要注意该函数实际用于两个明显有别的目的:pcMing工作室7e9s'd\\5X-PQk
1. 用于创建一个完成端口对象。
/{ L+kHA3j02. 将一个句柄同完成端口关联到一起。

)Vg~!D(I(n u`ss0最开始创建一个完成端口时,唯一感兴趣的参数便是NumberOfConcurrentThreads(并发线程的数量);前面三个参数都会被忽略。NumberOfConcurrentThreads参数的特殊之处在于,它定义了在一个完成端口上,同时允许执行的线程数量。理想情况下,我们希望每个处理器各自负责一个线程的运行,为完成端口提供服务,避免过于频繁的线程“场景”切换。若将该参数设为0,表明系统内安装了多少个处理器,便允许同时运行多少个线程!可用下述代码创建一个I/O完成端口:pcMing工作室(u3A;T/ql6jd)D ?Xd J
pcMing工作室(A~.T0Q%x7N,Q C

hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
pcMing工作室B]|Z;?ki;b9i
该语句的作用是返回一个句柄,在为完成端口分配了一个套接字句柄后,用来对那个端口进行标定(引用)。pcMing工作室8EI]0@3Ag&J R

一、工作者线程与完成端口
| Le8r U0成功创建一个完成端口后,便可开始将套接字句柄与对象关联到一起。但在关联套接字之前,首先必须创建一个或多个“工作者线程”,以便在I/O请求投递给完成端口对象后,为完成端口提供服务。在这个时候,大家或许会觉得奇怪,到底应创建多少个线程,以便为完成端口提供服务呢?这实际正是完成端口模型显得颇为“复杂”的一个方面,因为服务I/O请求所需的数量取决于应用程序的总体设计情况。在此要记住的一个重点在于,在我们调用CreateIoCompletionPort时指定的并发线程数量,与打算创建的工作者线程数量相比,它们代表的并非同一件事情。早些时候,我们曾建议大家用CreateIoCompletionPort函数为每个处理器
2KJh i4["_7b0都指定一个线程(处理器的数量有多少,便指定多少线程)以避免由于频繁的线程“场景”交换活动,从而影响系统的整体性能。CreateIoCompletionPort函数的 NumberOfConcurrentThreads参数明确指示系统:在一个完成端口上,一次只允许n个工作者线程运行。假如在完成端口上创建的工作者线程数量超出n个,那么在同一时刻,最多只允许n个线程运行。但实际上,在一段较短的时间内,系统有可能超过这个值,但很快便会把它减少至事先在 CreateIoCompletionPort函数中设定的值。那么,为何实际创建的工作者线程数量有时要比 CreateIoCompletionPort函数设定的多一些呢?这样做有必要吗?如先前所述,这主要取决于
%ms R'xi8VVr0应用程序的总体设计情况。假定我们的某个工作者线程调用了一个函数,比如Sleep或WaitForSingleObject,但却进入了暂停(锁定或挂起)状态,那么允许另一个线程代替它的位置。换言之,我们希望随时都能执行尽可能多的线程;当然,最大的线程数量是事先在CreateIoCompletionPort调用里设定好的。这样一来,假如事先预计到自己的线程有可能暂时处于停顿状态,那么最好能够创建比CreateIoCompletionPort的 NumberOfConcurrentThreads参数的值多的线程,以便到时候充分发挥系统的潜力。一旦在完成端口上拥有足够多的工作者线程来为 I/O请求提供服务,便可着手将套接字句柄同完成端口关联到一起。这要求我们在一个现有的完成端口上,调用CreateIoCompletionPort 函数,同时为前三个参数——FileHandle,ExistingCompletionPort和CompletionKey——提供套接字的信息。其中, FileHandle参数指定一个要同完成端口关联在一起的套接字句柄。ExistingCompletionPort参数指定的是一个现有的完成端口。 CompletionKey(完成键)参数则指定要与某个特定套接字句柄关联在一起的“单句柄数据”;在这个参数中,应用程序可保存与一个套接字对应的任意类型的信息。之所以把它叫作“单句柄数据”,是由于它只对
){ Sh*Qrp @0应着与那个套接字句柄关联在一起的数据。可将其作为指向一个数据结构的指针,来保存套接字句柄;在那个结构中,同时包含了套接字的句柄,以及与那个套接字有关的其他信息。pcMing工作室Wo8^([`Nw!~

pcMing工作室sIS(p7E'c

根据我们到目前为止学到的东西,首先来构建一个基本的应用程序框架。下面阐述了如何使用完成端口模型,来开发一个ECHO服务器应用。在这个程序中,我们基本上按下述步骤行事:pcMing工作室T3?5@I;b4o7F

pcMing工作室 n+U4G9[3n"ZrR'hV

1) 创建一个完成端口。第四个参数保持为0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程。pcMing工作室5}0N._4l.s*C;]t4K1`F
2) 判断系统内到底安装了多少个处理器。pcMing工作室i%K5kyD
3) 创建工作者线程,根据步骤2)得到的处理器信息,在完成端口上,为已完成的I/O请求提供服务。
z HcDU0`"OH3m04) 准备好一个监听套接字,在端口5150上监听进入的连接请求。
HBl@l)Wj h_05) 使用accept函数,接受进入的连接请求。
"PSj9~4y+{-N06) 创建一个数据结构,用于容纳“单句柄数据”,同时在结构中存入接受的套接字句柄。pcMing工作室St&KGE5Ku1c;ka
7) 调用CreateIoCompletionPort,将自accept返回的新套接字句柄同完成端口关联到一起。通过完成键(CompletionKey)参数,将单句柄数据结构传递给CreateIoCompletionPort。
:M"a0}K+~H08) 开始在已接受的连接上进行I/O操作。在此,我们希望通过重叠I/O机制,在新建的套接字上投递一个或多个异步WSARecv或WSASend请求。这些 I/O请求完成后,一个工作者线程会为I/O请求提供服务,同时继续处理未来的I/O请求,稍后便会在步骤3 )指定的工作者例程中,体验到这一点。
(fIOn*[eDS6sw^09) 重复步骤5 ) ~ 8 ),直至服务器中止。pcMing工作室5fm _&jA

pcMing工作室X1V%dg*n3z5g9j x3q@

二、完成端口和重叠I/OpcMing工作室CLQ'h!k8W5}
将套接字句柄与一个完成端口关联在一起后,便可以套接字句柄为基础,投递发送与接收请求,开始对I/O请求的处理。接下来,可开始依赖完成端口,来接收有关 I/O操作完成情况的通知。从本质上说,完成端口模型利用了Win32重叠I/O机制。在这种机制中,象WSASend和WSARecv这样的 Winsock API调用会立即返回。此时,需要由我们的应用程序负责在以后的某个时间,通过一个OVERLAPPED结构,来接收调用的结果。在完成端口模型中,要想做到这一点,需要使用GetQueuedCompletionStatus(获取排队完成状态)函数,让一个或多个工作者线程在完成端口上等待。该函数的定义如下:
!r!Z!K]WV{-x0

NpxI4Rg t0
BOOL GetQueuedCompletionStatus(pcMing工作室6T#]"Qw,t6~ B$C
    HANDLE CompletionPort,pcMing工作室-R{m!~ T |2GX.[
    LPDWORD lpNumberOfBytes,
l7H~ e&S3X@Us0    PULONG_PTR lpCompletionKey,pcMing工作室P],r h8dn
    LPOVERLAPPED* lpOverlapped,pcMing工作室0xJy&g w7o t
    DWORD dwMilliseconds
$dc7T?t0);
pcMing工作室1L(c3da~'n+S l:e
其中,CompletionPort参数对应于要在上面等待的完成端口。lpNumberOfBytes参数负责在完成了一次I/O操作后(如 WSASend或WSARecv),接收实际传输的字节数。lpCompletionKey参数为原先传递进入 CreateIoCompletionPort函数的套接字返回“单句柄数据”。如我们早先所述,大家最好将套接字句柄保存在这个“键”(Key)中。 lpOverlapped参数用于接收完成的I/O操作的重叠结果。这实际是一个相当重要的参数,因为可用它获取每个I/O操作的数据。而最后一个参数, dwMilliseconds,用于指定调用者希望等待一个完成数据包在完成端口上出现的时间。假如将其设为INFINITE,调用会无休止地等待下去。pcMing工作室ay|1r1H'r

TAG: Winsock
顶:56 踩:65
对本文中的事件或人物打分:
当前平均分:-0.27 (371次打分)
对本篇资讯内容的质量打分:
当前平均分:-0.39 (350次打分)
【已经有375人表态】
59票
感动
46票
路过
54票
高兴
40票
难过
33票
搞笑
44票
愤怒
51票
无聊
48票
同情
上一篇 下一篇
发表评论
换一张

网友评论仅供网友表达个人看法,并不表明本网同意其观点或证实其描述。

查看全部回复【已有0位网友发表了看法】

网络资源