完成端口 c# iocp

完成端口 c# iocp

完成端口听起来好像很神秘和复杂,其实并没有想象的那么难。这方面的文章在论坛上能找到的我差不多都看过,写得好点的就是CSDN.NET上看到的一组系列文章,不过我认为它只是简单的翻译了一下Network Programming for Microsoft Windows 2nd中的相关内容,附上的代码好像不是原书中的,可能是另一本外文书里的。我看了以后,觉得还不如看原版的更容易理解。所以在我的开始部分,我主要带领初学者理解一下完成端口的有关内容,是我开发的经验,其他的请参考原书的相关内容。
采用完成端口的好处是,操作系统的内部重叠机制可以保证大量的网络请求都被服务器处理,而不是像WSAAsyncSelect和WSAEventSelect的那样对并发的网络请求有限制,这一点从上一章的测试表格中可以清楚的看出。
完成端口就像一种消息通知的机制,我们创建一个线程来不断读取完成端口状态,接收到相应的完成通知后,就进行相应的处理。其实感觉就像WSAAsyncSelect一样,不过还是有一些的不同。比如我们想接收消息,WSAAsyncSelect会在消息到来的时候直接通知Windows消息循环,然后就可以调用WSARecv来接收消息了;而完成端口则首先调用一个WSARecv表示程序需要接收消息(这时可能还没有任何消息到来),但是只有当消息来的时候WSARecv才算完成,用户就可以处理消息了,然后再调用一个WSARecv表示等待下一个消息,如此不停循环,我想这就是完成端口的最大特点吧。
Per-handle Data 和 Per-I/O Operation Data 是两个比较重要的概念,Per-handleData用来把客户端数据和对应的完成通知关联起来,这样每次我们处理完成通知的时候,就能知道它是哪个客户端的消息,并且可以根据客户端的信息作出相应的反应,我想也可以理解为Per-Client handle Data吧。Per-I/O OperationData则不同,它记录了每次I/O通知的信息,比如接收消息时我们就可以从中读出消息的内容,也就是和I/O操作有关的信息都记录在里面了。当你亲手实现完成 端口的时候就可以理解他们的不同和用途了。
CreateIoCompletionPort函数中有个参数NumberOfConcurrentThreads,完成端口编程里有个概念WorkerThreads。这里比较容易引起混乱,NumberOfConcurrentThreads需要设置多少,又需要创建多少个WorkerThreads才算合适?NumberOfConcurrentThreads的数目和CPU数量一样最好,因为少了就没法利用多CPU的优势,而多了则会因为线程切换造成性能下降。WorkerThreads的数量是不是也要一样多呢,当然不是,它的数量取决于应用程序的需要。举例来说,我们在WorkerThreads里进行消息处理,如果这个过程中有可能会造成线程阻塞,那如果我们只有一个WorkerThread,我们就不能很快响应其他客户端的请求了,而只有当这个阻塞操作完成了后才能继续处理下一个完成消息。但是如果我们还有其他的WorkerThread,我们就能继续处理其他客户端的请求,所以到底需要多少的WorkerThread,需要根据应用程序来定,而不是可以事先估算出来的。如果工作者线程里没有阻塞操作,对于某些情况来说,一个工作者线程就可以满足需要了。

===========================================================

“完成端口”模型是迄今为止最为复杂的—种I/O模型。然而。假若—个应用程序同时需要管理为数众多的套接字,那么采用这种模型。往往可以达到最佳的系统性能,然而不幸的是,该模型只适用于以下操作系统(微软的):Windows NT和Windows 2000操作系统。因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候、而且希望随着系统内安装的CPU数量的增多、应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。要记住的一个基本准则是,假如要为Windows NT或windows 2000开发高性能的服务器应用,同时希望为大量套接字I/O请求提供服务(Web服务器便是这方面的典型例子),那么I/O完成端口模型便是最佳选择.

从本质上说,完成端口模型要求我们创建一个Win32完成端口对象,通过指定数量的线程对重叠I/O请求进行管理。以便为已经完成的重叠I/O请求提供服务。要注意的是。所谓“完成端口”,实际是Win32、Windows NT以及windows 2000采用的一种I/O构造机制,除套接字句柄之外,实际上还可接受其他东西。然而,本节只打算讲述如何使用套接字句柄,来发挥完成端口模型的巨大威力。使用这种模型之前,首先要创建一个I/O完成端口对象,用它面向任意数量的套接字句柄。管理多个I/O请求。要做到这—点,需要调用CreateIoCompletionPort函数。该函数定义如下:

HANDLE CreateIoCompletionPort(

HANDLE FileHandle,

HANDLE ExistingCompletionPort,

DWORD CompletionKey,

DWORD NumberOfConcurrentThreads

);

在我们深入探讨其中的各个参数之前,首先要注意意该函数实际用于两个明显有别的目的:

■用于创建—个完成端口对象。

■将一个句柄同完成端口关联到一起。

最开始创建—个完成端口的时候,唯一感兴趣的参数便是NumberOfConcurrentThreads并发线程的数量);前面三个参数都会被忽略。NumberOfConcurrentThreads 参数的特殊之处在于.它定义了在一个完成端口上,同时允许执行的线程数量。理想情况下我们希望每个处理器各自负责—个线程的运行,为完成端口提供服务,避免过于频繁的线程“场景”切换。若将该参数设为0,说明系统内安装了多少个处理器,便允许同时运行多少个线程!可用下述代码创建一个I/O完成端口:

CompetionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0)

该语加的作用是返问一个句柄.在为完成端口分配了—个套接字句柄后,用来对那个端

口进行标定(引用)。

1.工作者线程与完成端口

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

ExistingCompletionPort参数指定的是一个现有的完成端口。CompletionKey(完成键)参数则指定要与某个特定套接字句柄关联在—起的“单句柄数据”,在这个参数中,应用程序可保存与—个套接字对应的任意类型的信息。之所以把它叫作“单句柄数据”,是由于它只对应着与那个套接字句柄关联在—起的数据。可将其作为指向一个数据结构的指针、来保存套接字句柄;在那个结构中,同时包含了套接字的句柄,以及与那个套接字有关的其他信息。就象本章稍后还会讲述的那样,为完成端口提供服务的线程例程可通过这个参数。取得与其套字句柄有关的信息。

根据我们到目前为止学到的东西。首先来构建—个基本的应用程序框架。

程序清单8—9向人家阐述了如何使用完成端口模型。来开发—个回应(或“反射’)服务器应用

在这个程序中。我们基本上按下述步骤行事:

1)创建一个完成端口。第四个参数保持为0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程。

2)判断系统内到底安装了多少个处理器。

3)创建工作者线程,根据步骤2)得到的处理器信息,在完成端口上,为已完成的I/O请求提供服务。在这个简单的例子中,我们为每个处理器都只创建—个工作者线程。这是出于事先已经预计到,到时候不会有任何线程进入“挂起”状态,造成由于线程数量的不足,而使处理器空闲的局面(没有足够的线程可供执行)。调用CreateThread函数时,必须同时提供—个工作者线程,由线程在创建好执行。本节稍后还会详细讨论线程的职责。

4)准备好—个监听套接字。在端口5150上监听进入的连接请求。

5)使用accept函数,接受进入的连接请求。

6)创建—个数据结构,用于容纳“单句柄数据”。同时在结构中存入接受的套接字句柄。

7)调用CreateIoCompletionPort将自accept返回的新套接字句柄向完成端口关联到一起,通过完成键(CompletionKey)参数,将但句柄数据结构传递给CreateIoCompletionPort。

8)开始在已接受的连接上进行I/O操作。在此,我们希望通过重叠I/O机制,在新建的套接字上投递一个或多个异步WSARecv或WSASend请求。这些I/O请求完成后,一个工作者线程会为I/O请求提供服务,同时继续处理未来的I/O请求,稍后便会在步骤3)指定的工作者例程中。体验到这一点。

9)重复步骤5)—8)。直到服务器终止。

程序清单8。9完成端口的建立

StartWinsock()

//步骤一,创建一个完成端口

CompletionPort=CreateIoCompletionPort(INVALI_HANDLE_VALUE,NULL,0,0);

//步骤二判断有多少个处理器

GetSystemInfo(&SystemInfo);

//步骤三:根据处理器的数量创建工作线程,本例当中,工作线程的数目和处理器数目是相同的

for(i = 0; i <SystemInfo.dwNumberOfProcessers,i++){

HANDLEThreadHandle;

//创建工作者线程,并把完成端口作为参数传给线程

ThreadHandle=CreateThread(NULL,0,

ServerWorkerThread,CompletionPort,

0,&ThreadID);

//关闭线程句柄(仅仅关闭句柄,并非关闭线程本身)

CloseHandle(ThreadHandle);

}

//步骤四:创建监听套接字

Listen=WSASocket(AF_INET,S0CK_STREAM,0,NULL,

WSA_FLAG_OVERLAPPED);

InternetAddr.sin_famlly=AF_INET;

InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

InternetAddr.sln_port = htons(5150);

bind(Listen,(PSOCKADDR)&InternetAddr,sizeof(InternetAddr));

//准备监听套接字

listen(Listen,5);

while(TRUE){

//步骤五,接入Socket,并和完成端口关联

Accept =WSAAccept(Listen,NULL,NULL,NULL,0);

//步骤六创建一个perhandle结构,并和端口关联

PerHandleData=(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));

printf("Socket number %dconnectedn",Accept);

PerHandleData->Socket=Accept;

//步骤七,接入套接字和完成端口关联

CreateIoCompletionPort((HANDLE)Accept,

CompletionPort,(DWORD)PerHandleData,0);

//步骤八

//开始进行I/O操作,用重叠I/O发送一些WSASend()和WSARecv()

WSARecv(...)

  

爱华网本文地址 » http://www.413yy.cn/a/25101015/267891.html

更多阅读

GPSGATE怎么设置GPS共享端口 精 gpsgate pc破解版

GPSGATE怎么设置GPS共享端口 精——简介在我们用GPS的时候有时候需要共享几个端口GPS端口,那么怎么用GPSgate共享呢!这里说一下GPSgate设置端口共享的方法!GPSGATE怎么设置GPS共享端口 精——工具/原料?GPSgate?CNT?GPSGPSGATE怎

怎样设置打印机端口 打印机无法打印

笔者以 HP Deskjet 3748 打印机为例,详细介绍使用 HP Deskjet 3000 系列打印机进行网络打印的配置方法。怎样设置打印机端口——适用机型怎样设置打印机端口 1、HP Deskjet 3320、3323、3325、3420、3425、3538、3558、3658、3668、

如何查看某个端口被谁占用 如何查看adb 端口占用

如何查看某个端口被谁占用——简介我们在启动应用的时候经常发现我们需要使用的端口被别的程序占用,但是我们又不知道是被谁占用,这时候我们需要找出“真凶”,如何做到呢?如何查看某个端口被谁占用——工具/原料电脑一台如何查看某个

windows如何查看端口 windows非法进程

windows如何查看端口——简介简单介绍下如何查看windows中端口状态,以及tcp和udp连接的状态,通常我们可以通过查看tcp和udp的状态来查看一个连接是否处于断开或者卡住状态windows如何查看端口——工具/原料

教你路由器端口映射设置方法 思科路由器端口映射

教你路由器端口映射设置方法——简介本文就以TD-W89841N无线路由器为例做一个端口映射80端口号和映射3389端口号为例,希望能帮到局域网内想使用路由器做端口号映射到自己电脑的网友一个帮助。工具/原料教你路由器端口映射设置方法

声明:《完成端口 c# iocp》为网友哥就是嘚瑟分享!如侵犯到您的合法权益请联系我们删除