基于完成端口的网络服务应用程序开发
2009-07-13王爱平唐玄
王爱平 唐 玄
[摘要]由于互联网的普及,在线人数也越来越多,这就提高对服务器端应用程序性能的要求。因此,讲述windows平台上高性能网络服务应用程序的开发方法。
[关键词]完成端口IOCP网络服务
中图分类号:TP3文献标识码:A文章编号:1671-7597(2009)1110084-01
一、引言
随着网络的广泛应用,企事业单位、公司都需要开发网络服务应用程序,用于和客户进行交互。网络服务应用程序一般可采用B/S、C/S模式开发,其中,B/S模式已经广泛地应用在WEB应用程序上。然而,要实现实时、高性能的网络服务应用程序,仍需要采用C/S模式的开发方式。在windows操作系统环境下,使用C/S模式开发,主要考虑网络服务程序的服务器端程序性能,能支持大量用户并发。这就需要使用完成端口模型来开发服务器端程序。
二、使用完成端口
完成端口,又称Input/Output Completion Port(IOCP),是windows
操作系统中非常复杂的一种I/O模型。它可以同时为大量的SOCKET提供服务,因此它是windows操作系统中开发高性能网络应用服务器的首选。
创建完成端口时,windows操作系统会创建一个内核对象。使用完成端口时,将该内核对象与SOCKET进行关联,则SOCKET产生的事件会通知到该完成端口,此时,就可以通过完成端口对SOCKET进行相关操作了。
使用完成端口常用3个相关函数:
1.CreateIoCompletionPort(
HANDLE FileHandle,
HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey,
DWORD NumberOfConcurrentThreads);
本函数用来创建完成端口对象、关联完成端口对象和其他内核对象。
FileHandle是用来和完成端口对象关联的内核对象句柄;ExistingCo
mpletionPort是已经创建好的完成端口对象句柄,FileHandle所指定的内核对象I/O事件将会通知到该完成端口对象;CompletionKey是完成键,可以为任何用户指定的值,其目的是程序可以借此参数和完成端口之间传递值,方便与完成端口之间的通信。NumberOfConcurrentThreads用于指定同时可以并发的线程数量,取0表示并发线程数据为系统中CPU的数量。
2.GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDOWRD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped,
DWORD dwMilliseconds);
本函数用来获取完成端口状态。调用本函数后,线程挂起。当与该完成端口相关联的内核对象I/O操作完成时,唤醒线程。
CompletionPort为完成端口对象句柄;lpNumberOfBytestTransferred
是I/O操作中传输的字节数;lpCompletionKey是调用CreateIoCompletionP
Ort时传入的CompletionKey参数;lpOverlapped为LPOVERLAPPED的指针类型,是操作系统管理完成端口需要的系统类型,通常I/O传输缓冲区是由该结构指定的;dwMilliseconds指定超时时间。
3.PostQueruedCompletionStatus(
HANDLE CompletionPort,
DWORD dwNumberOfBytesTransferred,
ULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped);
本函数用来向完成端口发送信息以改变程序流程。
三、系统设计
服务器程序主线程程序流程:1.调用CreateIoCompletionPort函数创建完成端口;2.调用_beginthreadex函数创建一定数量工作者线程ThreadProc;3.调用socket函数创建侦听套接字并使用listen函数在指定端口开始侦听;4.调用WSAAccept函数等待客户端连接(此时主线程将挂起)。当客户端连接成功,则创建完成键(一般包含客户端相关信息),关联完成端口与WSAccept返回的新套接字句柄,并向该套接字句柄投递一个或多个异步WSARecv或WSASend请求。循环执行本操作,直至程序结束。
服务器程序工作者线程程序流程:调用GetQueuedCompletionPort函数,线程挂起。当操作系统唤醒该线程时,GetQueuedCompletionPort返回I/O传输相关信息。此时,根据返回相关参数值执行相应程序。
四、改进措施
1.使用AcceptEx。由于WSAAccept调用之后,主线程将会被阻塞,直到有客户端连接时才被唤醒;而且,当某个客户端连接的时候,独占主线程,其它客户端将无法连接服务器。因此,这里可以采用异步函数AcceptEx。使用AcceptEx函数可以同时创建多个套接字,并使它们处于等待用户连接状态。而且,AcceptEx也采用了OVERLAPPED结构,它也可以和完成端口相关联,可以在工作者线程中统一处理客户端连接事件。这样,就提高了接受客户端连接的效率。
2.使用线程池。microsoft推荐使用的线程数为CPU数量乘以2加1,这是为了避免线程频繁切换引起的系统开销,这也是完成端口的优点。但实际上,服务请求所需数量不应用永远取此值,它取决于应用程序的总体设计情况。如果系统中现有工作者线程均处于挂起或锁定状态,这时就需要额外创建新的工作者线程来响应I/O操作,从而提高整个系统的运行效率。
3.使用内存池。在新用户连接服务器时,需要为每一个用户分配一个结构体存储OVERLAPPED结构、数据传输缓冲区、完成键等信息。如果每来一个用户,就分配一块内存,服务器就需要频繁地开辟新空间;而如果每一个用户断开连接时,服务器就需要频繁地释放所占内存。这样会导致系统性能下降。因此,服务器应用程序可以预先分配一定数量内存,当新用户连接服务器时,从中获取一块标识为“未使用”的内存用于保存信息,标识为“已占用”;当该用户断开与服务器连接时,标识该块内存为“未使用”。这样,就可以有效地避免了服务器频繁地分配与释放内存。
五、结束语
经测试,使用完成端口设计的服务器端应用程序可以充分发挥服务器的性能,支持海量用户连接与传输信息。
参考文献:
[1]Jeffrey Richter,windows核心编程,机械工业出版社[M].北京,2005年9月.
[2]Jeffrey Richter,windows高级编程指南,清华大学出版社[M].北京,2001年12月.