Windows下大容量服务器软件的开发与设计

2012-06-13 10:03任建华内蒙古体育职业学院内蒙古呼和浩特010051
科技传播 2012年2期
关键词:监听缓冲区线程

任建华内蒙古体育职业学院,内蒙古呼和浩特 010051

Windows下大容量服务器软件的开发与设计

任建华
内蒙古体育职业学院,内蒙古呼和浩特 010051

互联网的普及使得同时访问一个服务器的用户越来越多,本文通过详细对比几种Winsock I/O方法的优缺点,分析了在服务器开发中如何使用IOCP技术、重叠I/O技术、线程池技术、内存池等技术,并通过性能测试表明此服务器能够处理海量套接字连接请求,可以使服务器具有极佳的性能和强大的扩展能力,为开发大容量、可伸缩性服务器打下了良好的基础。

服务器软件;完成端口;Winsock;多线程

0 引言

服务器是指网络中能对其他机器提供某些服务的计算机系统软件或者设备。服务器作为网络节点,存储、处理网络上80%的数据、信息,因此也被称为网络的灵魂。对于用户来说,一台计算机所能承受的负荷是多多益善的。微软公司winsock2中引入了内核级的完成端口(IOCP)模型,IOCP(I/O Completion Ports,I/O完成端口)技术是应用程序使用线程池处理异步I/O请求的一种机制,作为Windows服务平台上一种比较成熟的I/O处理技术,完成端口模型可以利用为数不多的线程为成千上万的套接字同时提供网络服务,随着UNIX操作系统的广泛应用,套接字成为当前最流行的网络通信应用程序接口之一。

1 服务器总体设计

完成端口技术是伸缩性最好的一种I/O模型,能随着系统内安装的CPU数量的增多而线性的提升服务器的性能。把完成端口模型封装成一个比较普通的C++类,只要继承这个类,改写其中的两个虚函数(HandleData和DataAction)就可以满足各种服务器的需要。通过用AcceptEx代替accept和使用LOOKASIDE LIST来管理内存,服务器的性能有了较大的提升。

1.1 本服务器的工作流程如下图所示:

1.2 服务器各功能模块的实现

服务器主要分为2个模块:主窗口模块、服务器模块。主窗口模块负责初始化和启动服务器模块。服务器模块为整个程序的核心,利用多个I/O工作线程在完成端口上处理来自众多客户端的异步I/O请求。

1.2.1 主窗口模块

主窗口模块主要用于初始化,同时负责启动、设置服务器并设置IP地址和端口号。

1.2.2 服务器模块

此模块中,CompletionPortModel类是这个服务器的核心。这个类使用了IOCP技术,它可以非常高效的为大量客户提供服务。CompletionPortModel类有多个I/O工作线程在完成端口上处理异步I/O调用,当网络事件发生时,这些线程调用类中的虚函数(HandleData和DataAction)来为事件服务。CompletionPortModel类接收到启动命令后,首先创建监听线程,再由监听线程创建I/O工作线程。服务器启动期间,监听线程一直运行,为I/O工作线程提供服务。

1 )完成端口类的声明

2 )开始服务

成员函数Init用于初始化,创建完成端口、创建完成端口处理线程,首先调用类成员函数InitWinsock初始化Winsock、建立一个监听套接字m_ListenSocket,并将此套接字同完成端口关联起来,获取AcceptEx指针。其次再调用BindAndListenSocket()将监听套接字m_ListenSocket绑定到主窗口模块输入的本地IP地址和端口。并置于监听模式。

3 )内存管理

(1)使用旁视列表管理内存

每当有一个新连接到来的时候,服务器程序都要为该程序创建一个结构体(单句柄数据)用于存储该客户端的socket信息,而在每一次的WSASend和WSARecv中,工作线程也要向系统投递一个重叠类型的结构体(单IO数据)用于数据的收发。为了高效的分配和释放数据,采用Lookaside List(旁视列表)来管理内存的分配与回收。

(2)内存资源异常处理

应用服务器管理众多客户端I/O请求时,数据缓冲区很可能会被锁定。操作系统对锁定内存的数量有限制,达到这个限制时,重叠操作就会以WSAENOBUFS错误失败。实际设计中,当服务器处理大量并发客户端时,可以在每个连接上投递一个0字节的接收操作,这样就不会有内存被锁定。0字节的接收操作完成以后,服务器可以执行一个非阻塞的接收来获取缓冲区中的所有数据。

4 )连接管理

(1)使用AcceptEx管理客户端连接

服务器端程序在接受客户端连接的时候通常调用Accept函数,该函数是一个阻塞函数,当该Accept函数没有返回时又有新客户端连接请求已经发过来时,新客户端就要处于等待。在本程序中,把FD_ACCPET事件和一个Event关联起来,然后用WaitForSingleObject等待这个Event,当已经发出的AcceptEx调用数目耗尽而又有新的客户端需要连接时,FD_ACCEPT事件将被触发,Event变为已传信状态,WaitForSingleObject返回,然后调用成员函数PostAccetpEx()重新发出10个AcceptEx调用。

(2)恶意客户端连接问题

恶意的客户连接是指客户端出现坏链接,有的终端既不发送数据,也不关闭连接,就会造成AcceptEx投递的大量重叠操作不能返回,为了满足其他客户端请求,服务器不得不再继续投递更多的接收I/O,占用了大量的系统资源,为了避免这个事件发生,应用服务器记录了所有AcceptEx投递的未决I/O请求,在现场中定时遍历它们,对每个客户端套接字以SO_CONNECT_TIME为参数调用getsockop函数,检查套接字建立的时间,如果某个连接没有按时收发过数据,则通过使用PostQueuedCompletionStatus()函数强制关闭该套接字发送强制关闭。

5 )数据处理

(1)基本的数据处理

为了测试服务器能否响应大数量的客户端连接,设计了回应服务器,通过自定义枚举型数据结构,用来标识套接字的I/O操作类型,对应单IO数据结构中的一个参数,这样HandleData()和DataAction()即可根据详细的操作类型来进行下一步的IO操作。当服务器端完成了用WSARecv数据接收完毕时,就调用WSASend把刚收到的数据重新发送给客户端(即将IO操作标志设为IoWrite);如果上一次服务器端完成了WSASend将数据发送完成时,就将后续的操作标志设为关闭(IoEnd)。

(2)包重新排序问题

使用I/O完成端口的操作会按照它们被提交的顺序完成,但是线程调度可能会影响最终结果。本文采用向提交的缓冲区对象中添加序列号的方法来解决上面的问题,如果缓冲区序列号是连续的,就处理缓冲区中的数据,因此有错误序列号的缓冲区要被保存下来,以便今后使用。

3 服务器的性能测试

在本机(Intel Core2 Duo CPU T7500 2.2GHz 2GB内存)上同时运行了服务器和虚拟客户端。测试时,虚拟客户端分别启动了100,、200、300、400、500、600、700、800、900、1000、1100、1200、1300、1400、1500、1600、1700、1800、1900、2000个线程,代表不同数量的客户端同时访问服务器。

图2 服务器测试结果

从图2可以看出,随着连接客户数量的增多,服务器对CPU的占用率并没有急速增长。而是缓慢增长,这是服务器伸缩性好的一种表现。另外,服务器对内存的消耗量也是稳定的呈线性缓慢增长,说明服务器在内存消耗上是高效的,稳定的。本文给出的设计实例,已在上调试通过,该方法简单实用,为分析提供了参考资料,有助于进一步研究以为内核的具有丰富硬件资源的操作系统的移植。

[1]Anthony Jones,Windows网络编程技术[M].北京:机械工业出版社,2000,89-242.

[2]王艳平,张越.Windows网络与通信程序设计.北京:人民邮电出版社,2006:13-99.

[3]马金鑫,袁丁.基于IOCP的高并发通信服务器的设计与实现[J].通信技术,2009(7):248-250.

[4]吴星,黄爱萍.用完成端口实现可扩展的服务器应用[期刊论文].计算机科学.2002,29(11):144-164.

TP31

A

1674-6708(2012)59-0129-02

猜你喜欢
监听缓冲区线程
千元监听风格Hi-Fi箱新选择 Summer audio A-401
嫩江重要省界缓冲区水质单因子评价法研究
网络监听的防范措施
浅谈linux多线程协作
应召反潜时无人机监听航路的规划
关键链技术缓冲区的确定方法研究
局域网监听软件的设计
基于上下文定界的Fork/Join并行性的并发程序可达性分析*
Linux线程实现技术研究
地理信息系统绘图缓冲区技术设计与实现