IOCP机制与网络代理服务器实现方法
]IOCP是一种在Windows服务平台上比较成熟的I/O方法,针对大量并发客户[摘要摘要]
请求问题,采用IOCP多线程控制模型建立高效网络代理服务器思想,能够较好地代理服务器中的多线程竞争问题。本文在比较基于该模型的两种编程方案的基础上,给出了基于Windows2000的网络代理服务器的设计与代理实现过程。
关键词:完成端口重叠I/O多线程
1、引言
网络代理服务器的主要作用是将客户端的访问请求转发到远程服务端,并将应答信息回传给客户端。随着Internet应用的迅速发展和应用,代理服务器的作用及其性能已显得越来越重要了。
一个好的代理服务器应该具备高可靠性和可扩展性并能在不丧失性能的前提下,可同时为多个客户端提供服务。开发代理服务器程序的难点在于代理程序应该具有可扩展性,并能处理从单个连接到乃至数千个连接请求。代理服务程序一般采用“以客户/一线程”的工作模式,即为每一个连接创建一个线程。然而,当请求连接的客户增多,线程的数目就会大量增加,因此,操作系统必须花费额外的资源和时间来协调众多的线程,一旦处理不当,将会造成系统资源负荷过重,甚至可能导致整个系统瘫痪。
针对上述问题,利用IOCP机制可以较好地解决代理服务器的多线程竞争所带来的问题。
2、IOCP机制
IOCP(I/OCompletionPort输入/输出完成端口)是一种能能够合理利用与管理多线程的机制。该机制使用完成端口,用一定数量的线程处理重叠I/O的技术,帮助处理大量客户端请求的网络代理服务问题,特别适合于开发像代理服务器一类的应用程序,并可使系统的性能达到较佳状态。IOCP模型结构如图1所示。
图1完成端口模型结构
完成端口模式要求创建一个Win32完成端口对象来对重叠I/O请求进行管理,并通过创建一定数量的工作者线程(WorkThread),来为已经完成的重叠I/O请求提供服务。其实,可以把完成端口看成系统维护的一个队列,操作系统把重叠I/O操作完成的事件通知放入该队列,由于是“操作完成”的事件通知,故取名为“完成端口”。一个完成端口被创建以后,可以和多个文件句柄进行关联
件句柄可以是真正的文件句柄,也可以是Socket句柄或命名管道),并在关联后的句柄上进行重叠I/O操作。当I/O操作完成后,一个重叠I/O完成的事件通知就会被排在此端口的完成队列上,此时,某个工作者线程将会被唤醒来为完成端口服务,执行特定的处理工作。一般来说,一个应用程序可以创建多个工作者线程来处理端口上的通知事件,工作者线程的数量依赖于程序的具体需要。
3、编程方法
利用IOCP机制的编程方式不是唯一的,我了解到以下两种方法:
方法1:使用CreateCompletionPort()函数
函数的形式定义如下:
HANDLECreateCompletionPort
(
HANDLEFileHandle,
//文件句柄(可以是Socket,命名管道等)
HANDLEExistingCompletionPort,//存在的完成端口句柄
DWORDCompletionKey,
);
首先,使用该函数实现两项任务:创建一个完成端口对象,用此函数将所要用到的文件句柄关联到完成端口对象上。然后,创建一个或多个工作者线程来处理完成通知事件,每个线程都可以循环调用GetQueuedCompletionStatus()函数,用以检查完成端口上的通知事件。该函数的形式定义如下:
BOOLGetQueuedCompletionStatus
(
HANDLECompletionPort,//关联的完成端口
LPDWORDlpNumberOfBytesTransferred,
//I/O完成后得到的字节数
LPDWORDlpCompletionKey,//完成键
LPOVERLAPPED*lpOverlapped,
//重叠I/O操作所设的Overlapped结构
DWORDdwMinlliseconds
//等待的时间,取INFINITE时则一直等待
);
一旦该函数得到了完成端口上的通知事件,则等待在完成端口上的工作者线//完成键DWORDNumberOfConcurrentThreads//并发的线程数量
程就会被唤醒,并根据I/O操作的类型做出相应的处理。
方法2:使用BindIoCompletionCallBack()函数
函数的形式定义如下:
BOOLBindIoCompletionCallback
(
HANDLEFileHandle,
//文件句柄(可以是Socket,命名管道等)
LPOVERLAPPED_COMPLETION_ROUTINEFunction,
//回调函数
ULONGFlags
);
其中,回调函数的形式定义如下:
VOIDCALLBACKWorkthreadFunction
(
DWORDdwErrorCode,//错误码
//Overlapped结构DWORDdwNumberOfBytesTransfered,//传输的字节数LPOVERLAPPEDlpOverlapped
);
BindIoCompletionCallBack()函数将FileHandle与完成端口相绑定,绑定工作完成后,一旦FileHandle上有重叠的I/O操作完成,操作系统就会自动调用已与FileHandle相绑定的回调函数来对重叠I/O完成后得到的数据进行相应的处理。
上述两种方法各有优点,前者由程序员自己创建完成端口,自行创建、管理工作者线程,并能自主控制系统的流程;而采用后者时,开发者无需自行创建完成端口对象及工作者线程,而应用程序调用BindIoCompletionCallBack()函数时,Windows系统会自动创建和管理一个完成端口对象和若干个工作者线程,以减轻对线程创建、挂起和唤醒等一系列管理操作的负担。
4、代理服务器实例
代理服务器模型如图2所示://保留(为0)
图2代理服务器模型图
在设计具体实现方案时,为充分利用Windows2000
的线程管理机制,减少
编程及调试的工作量,本实例采用了第二种方法。
代理主要算法
1)创建一个监听套接字Listener;
2)调用BindIoCompletionCallBack()函数,将监听套接字与I/O完成端口进行关联;
3)创建并初始化若干个与客户端连接的套接字S_ClientToProxy
_i;
4)创建并初始化若干个与目标服务器端连接的套接字S_ProxyTo
Server_j;
5)利用AcceptEx函数接收所到达的客户端请求,并使用某个已建好的客户端与代理之间的套接字来处理,并调用BindIoCompletion
CallBack()函数将此套接字与完成端口相关联,一旦有客户端请求完成,就启动回调函数进行相应处理;
6)调用BindIoCompletionCallBack()函数,将某个已建立好的代理与目标服务器之间的套接字与完成端口相关联,一旦有I/O操作完成,就启动回调函数进行相应处理;
7)返回5)直到收到强制退出信号,关闭对应套接字并结束代理。
代理过程
启动程序之后,利用IOCP机制开始进行代理。
工作者线程执行回调函数WorkThreadFunction()流程如图3所示,具体工作步骤如下:
1)检查是哪种I/O操作完成;
2)通过传递的Overlapped结构指针,判断是哪一种I/O操作完成;
3)根据判断结果,选择不同的分支作适当的处理;
如果是从客户端读信息操作完成,则工作者线程发起向服务器的写操作,将
所读的信息发往目标服务器;
图3工作线程执行的回调函数流程图
如果是写信息到服务器操作完成,则工作者线程发起从服务器端的读操作,将返回的信息从目标服务器取到代理服务器;
如果是从目标服务器端读信息操作完成,则工作者线程向客户端的写操作,将信息发往客户端;
如果是写信息到客户端操作完成,则工作者线程结束本次代理过程。
4)返回到1)继续检查是否有重叠I/O操作完成。
由于整个代理过程均是采用重叠I/O操作,所以代理服务器能够同时并行执行读/写操作,异步处理大量客户请求,并且最大限度地提高了系统的性能和速度。
编写网络代理应用程序的难点在于程序的“可扩展性”即如何开发出大容量且能处理大量并发SocketI/O请求的高性能代理应用程序。IOCP机制通过完成端口对象来对重叠I/O请求进行管理,并且利用多线程来处理重叠I/O操作完成后得到的数据,是一种与Win32Socket结合度较高的实现高效率I/O的有效方法。
实际应用表明:利用IOCP机制实现的网络代理应用程序能够针对大量的客户请求进行相应代理,且在速度和性能上体现出其良好的特征,不失为一种实现网络代理的好选择。