陈春娇
摘要:该文比较详细地介绍了循环和并发服务器的概念、工作原理及循环和并发服务器的算法,从工作原理、工作流程等方面进行阐述两者的区别,并分别用一个循环服务器程序和并发服务器程序实例代码加以说明。
关键词:循环服务器;并发服务器;算法;应用
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2012)32-7706-04
1循环和并发服务器的概念
网络服务器有循环服务器和并发服务器两种。循环服务器:循环服务器在同一个时刻只可以响应一个客户端的请求;并发服务器:并发服务器在同一个时刻可以响应多个客户端的请求。
网络服务器有循环服务器和并发服务器两种。连接性问题是传输协议的中心,而客户使用这个传输协议访问某个服务器。TCP/IP协议族给用提供了两种传输协议,可以使用面向连接的传输(TCP)或无连接的传输(UDP)。因此,可以将服务器划分为四种一般的类型:循环无连接、循环面向连接、并发无连接、并发面向连接。
2循环和并发服务的工作原理
2.1循环服务器的工作原理及及流程
1)循环无连接(UDP)的服务器的工作原理及应用范围
循环无连接的服务器的算法
①创建套接字并将其绑定到所提供服务的熟知端口上。
②重复地读取来自客户的请求,构造响应,按照应用协议向客户发回响应。
UDP循环服务器的实现非常简单:UDP服务器每次从套接字上读取一个客户端的请求,处理,然后将结果返回给客户机.
可以用下面的算法来实现.
socket(...);
bind(...);
while(1)
{
recvfrom(...);
process(...);
sendto(...);
}
因为UDP是非面向连接的,没有一个客户端可以老是占住服务端.只要处理过程不是死循环,服务器对于每一个客户机的请求总是能够满足.
2)循环面向连接(TCP)的服务器的工作原理及应用范围
循环面向连接的服务器的算法
①创建套接字并将其绑定到它所提供服务的熟知端口上。
②将该端口设置为被动模式,使其准备为服务器所用。
③从该套接字上接受下一个连接请求,获得该连接的新的套接字。
④重复地读取来自客户的请求,构造响应,按照应用协议向客户发回响应。
⑤当与某个特定客户完成交互时,关闭连接,并返回步骤3以接受新的连接。
TCP循环服务器的实现也不难:TCP服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
}
TCP循环服务器一次只能处理一个客户端的请求.只有在这个客户的所有请求都满足后,服务器才可以继续后面的请求.这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了.因此,TCP服务器一般很少用循环服务器模型的.
2.2并发服务器的工作原理及应用范围
1)并发面向连接(TCP)的服务器的工作原理及应用范围
并发服务器:TCP服务器
为了弥补循环TCP服务器的缺陷,人们又想出了并发服务器的模型.并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个子进程来处理.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
if(fork(..)==0)
{
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
exit(...);
}
close(...);
}
TCP并发服务器可以解决TCP循环服务器客户机独占服务器的情况.不过也同时带来了一个不小的问题.为了响应客户机的请求,服务器要创建子进程来处理.而创建子进程是一种非常消耗资源的操作.
2)并发无连接的服务器的工作原理及应用范围
不同于顺序服务器,并发服务器就要能在一个时间为多个客户端提供服务。例如,一个聊天服务器可能服务一个特定的客户端数小时──在停止为这个客户端服务之前服务器不能等待,除非是在等待一下个客户端到来之前的间隙才能等待。
我们将提供服务从守护进程移至它自己的服务进程。然而,因为每个子进程都继承所有打开的文件(套接字被像文件一样处理),新进程不仅继承“accept()返回的句柄,”那是指调用accept返回的套接字;新进程也继承顶级套接字,这是顶级进程一开始打开的套接字。
然而,服务进程不需要这个套接字,应该立即关闭(close)它。同样的,守护进程不再需要accept()返回的套接字,不仅应该,还必须关闭(close)它──否则,那迟早会耗尽可用的文件描述符。
在服务进程完成服务之后,它将关闭accept()返回的套接字。它不会返回到accept,而是退出进程。
在UNIX?上,一个进程并不真正的退出,而是返回至父进程。典型情况中,父进程等待(wait)子进程,并取得一个返回值。但是,我们的守护进程不能简单的停止或等待,那有违建立其它进程的整个目的。但是如果从不使用wait,它的子进程可能会成为僵尸──不再有功用可仍然徘徊着。
出于那样的原因,守护进程需要在初始化守护进程阶段设置信号处理程序。至少要处理信号SIGCHLD,这样守护进程可以从系统清除僵尸返回值并释放僵尸占用的系统资源。
这是现在我们的流程图包含一个进程信号框的原因,它不与任何其它框相连接。顺便说一句,许多服务器程序也处理SIGHUP,作为超级用户发出的要求重读配置文件的信号。这允许我们不必终止或重启服务器程序就改变设置。
3循环服务器和并发服务器的比较及应用场景
3.1并发服务器和循环服务器比较
一般来说,并发服务器更难设计和构建,其最终的代码也更复杂并且难于修改。然而,大多数程序员还是选择了并发实现的方法,因为循环服务器会在分布式应用中引起不必要的时延,而且可能会成为影响许多客户应用程序的性能瓶颈。我们概括如下:使用循环方法实现的服务器易于构建和理解,但这样的结果会使其性能很差,因为这样的服务器要使客户等待服务。相反,以并发方法实现的服务器难于设计和构建,但却有较好的性能。
3.2循环服务器和并发服务器的应用场景
1)循环的和并发的:如果循环方案产生的响应时间对应用来说足够充分,就可以使用循环;否则需要并发;2)真正的和表面上的并发性:如果线程或切换环境的开销大,服务器需要在多个连接之间共享或者交换数据,用单线程;使用线程开销不大或者要得到最大并发性,使用多进程;3)面向连接的和无连接的:如果应用协议处理了可靠性问题,或者应用在局域网环境内,使用无连接的传输。
4循环和并发服务器程序实例
循环服务器容易设计、实现和维护,但是性能差;并发服务器难以构建和设计,但是性能好,响应快。
4.1循环服务器的应用程序实例:
循环式服务器模型:(以时间服务程序time为例)
1)服务器端主函数部分代码分析
①前期准备部分
int
main(intargc,char*argv[])
{
structsockaddr_infsin; /*用于存储客户端地址信息 */
char *service="time"; /*用于存储服务名或者端口号*/
char buf[1];
int sock;
time_t now; /*当前时间*/
un小ignedint alen; /*客户端地址长度*/
switch(argc)
{
case 1:
break;
case 2:
service=argv[1];
break;
default:
errexit("usage:UDPtimed[port]\n");
}
/*argc=1时,表示只有一程序名称。argv[0]指向输入的程序路径及名称;argc=2,表示除了程序名外还有一个参数。argv[0]指向输入的程序路径及名称。argv[1]指向参数的字符串;Default报错。*/
sock=passiveUDP(service);
/*调用已定义的passiveUDP函数创建套接字*/
②实际处理连接通信服务部分
while(1){
alen=sizeof(fsin);
if(recvfrom(sock,buf,sizeof(buf),0,(structsockaddr*)&fsin,&alen)<0)
errexit("recvfrom:%s\n",strerror(errno));
/*读取来自客户端发起的请求,若返回值小于0就读取失败调用errexit函数报错*/
(void)time(&now); /*获取时间*/
now=htonl((unsignedlong)(now+UNIXEPOCH));
/*将本地字节顺序转换成客户端能识别的统一的网络字节顺序*/
(void)sendto(sock,(char*)&now,sizeof(now),0,(structsockaddr*)&fsin,sizeof(fsin));
/*调用sendto函数将时间信息发送给客户端*/
}
③ passivesock函数模块关键代码分析:(含sock、bind、listen)
memset(&sin,0,sizeof(sin)); /*将sin初始化置零*/
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=INADDR_ANY; /*将服务器接入点置为任意inaddr_any*/
if(pse=getservbyname(service,transport))/*尝试通过service获取地址*/
sin.sin_port=htons(ntohs((unsignedshort)pse→s_port)+portbase);
/*htons/ntohs本地字节顺序与网络字节顺序的相互转换;设置全局的portbase可以重新分配端口值,映射到更高的端口,这样测试就不会与原来的重复,还能运行原来的服务的,便于测试*/
s=socket(PF_INET,type,ppe→p_proto);
if(s<0)
errexit("can'tcreatesocket:%s\n",strerror(errno));
/*创建套接字,成功则返回描述符S,失败则报错*/
bind(s,(structsockaddr*)&sin,sizeof(sin)) /*绑定端口地址*/
listen(s,qlen) /*调用listen函数设为监听状态*/
5结束语
该文共分为四个部分,第一部分比较详细地介绍了循环和并发服务器的概念;第二部分详细介绍循环和并发服务器的工作原理及算法,第三部分主要讲循环和并发服务器比较及应用场合,第四部分是举出循环服务器和并发服器的程序实例代码并进行分析。在撰写这篇论文过程中,明显感觉该学科的的难度,但正是在这个摸索的过程中,通过实践对该课程的知识有了更深层的理解。
参考文献:
[1]ComerDE,StevensDL.用TCP/IP进行网际互连[M].赵刚,林瑶,蒋慧,译.北京:电子工业出版社,2011.
[2]杜煜.计算机网络基础[M].北京:人民邮电出版社,2002.
[3]RameshS,PerrorsHG.Amultilayerclient-serverqueueingnetworkmodelwithsynchronousandasynchronousmessages[J].IEEETransactiononSoftwareEngineering,2000,26(11):1086-1100.
[4]洪英.Linux中帐号的安全管理方法[J].科技创新导报,2008(4):12.
[5]关于main函数的(int_argc,char_argv[]).51CTOLinux编程学习[Z].
[6]武奇生.网络与TCP/IP协议[M].西安:西安电子科大出版社,2010.