TCP/IP网络重复型服务器通信软件的设计
2008-02-23 05:37:37来源:互联网 阅读 ()
并发服务器和重复服务器的区别
一般TCP/IP服务器通信软件都是并发型的,即是由一个守护进程负责监听客户机的连接请求,然后再由守护进程生成一个或多个子进程和客户机具体建立连接以完成通信,其缺点是随着连接的客户机数量的增多,生成的通信子进程数量会越来越多,在客户机数量较多的应用场合势必影响服务器的运行效率。一般的重复服务器指的是服务器在接收客户机的连接请求后即和之建立连接,然后要在处理完和客户机的通信任务后才能再去接收另一客户机的请求连接,其长处是不必生成通信子进程,缺点是客户机在每次通信之前都要和服务器建立连接,开销过大,不能用于随机的数据通信和繁忙的业务处理。
本文提出的新型的重复型服务器不同于一般的重复服务器,他摒弃了上述两类服务器的缺点综合其长处,该服务器通信软件具备一般重复服务器的特征但又能处理客户机的随机访问,在客户机数量多且业务繁忙的应用场合将发挥其优势。重复型服务器通信软件只用三个进程就可完成和任何客户机建立连接,并始终保持这些连接。
重复型服务器通信软件和客户机建立连接的方法
基本思路
当第一台客户机向服务器请求连接时,服务器的守护进程和之建立初始连接(L0),客户机利用L0向服务器发送两个端口号,守护进程将客户机的IP地址和端口号登记在共享内存的记录中,然后关闭L0。由守护进程生成的两个通信子进程从共享内存中获得客户机IP地址及端口号后,分别向客户机请求连接,建立一个从客户机读的连接(L1)和一个往客户机写的连接(L2),并将两个连接的套接字的句柄记录在共享内存中。当另一台客户机请求连接时,守护进程不再生成通信子进程,只是将客户机IP地址和端口号同样登记在共享内存中。通信子进程在一个大循环中先查询共享内存中是否有新的记录,假如有则和这一台客户机建立连接,然后轮询任何已建立的连接的读套接字,查看是否有数据可读,有则读取数据,同时标明该数据是从共享内存中的哪条记录上的读套接字中获得的,再由另一个通信子进程根据这个记录的编号从共享内存中获得对应的写套接字,最后将结果数据往该套接字写往客户机。 2.2 建立连接
⑴ 服务器通信软件的初始进程首先建立公用端口上的套接字,并在该套接字上建立监听队列,同时生成一个守护进程(Daemon)tcp_s,然后初始进程就退出运行。守护进程在函数accept处堵塞住直到有客户机的连接请求,一有连接请求即调用server函数处理,然后继续循环等待另一台客户机的请求。因为TCP/IP在连接被拆除后为了避免出现重复连接的现象,一般是将连接放在过时连接表中,连接在拆除后若要避免处于TIME_WAIT状态(过时连接),可调用setsockopt配置套接字的linger延时标志,同时将延时时间配置为0。服务器在/etc/services文档中要登记一个全局公认的公用端口号:tcp_server 2000/tcp。
struct servent *sp; struct sockaddr_in peeraddr_in,myaddr_in; linkf=0; sp=getservbyname("tcp_server","tcp"); ls=socket(AF_INET,SOCK_STREAM,0); /* 创建监听套接字 */ myaddr_in.sin_addr.s_addr=INADDR_ANY; myaddr_in.sin_port=sp->s_port; /* 公用端口号 */ bind(ls,&myaddr_in,sizeof(struct sockaddr_in)); listen(ls,5); qid3=msgget(MSGKEY3,0x1ff); /* 获得消息队列的标志号 */ qid4=msgget(MSGKEY4,0x1ff); signal(SIGCLD,SIG_IGN); /* 避免子进程在退出后变为僵死进程 */ addrlen=sizeof(struct sockaddr_in); lingerlen=sizeof(struct linger); linger.l_onoff=1; linger.l_linger=0; setpgrp(); switch(fork()){ /* 生成Daemon */ case -1:exit(1); case 0: /* Daemon */ for(;;){ s=accept(ls,&peeraddr_in,&addrlen); setsockopt(s,SOL_SOCKET,SO_LINGER,&linger,lingerlen); server(); close(s); } default: fprintf(stderr,"初始进程退出,由守护进程监听客户机的连接请求.\n"); } |
⑵ 客户机以这样的形式运行通信程式tcp_c:tcp_c rhostname,rhostname为客户机所要连接的服务器主机名。客户机上的/etc/services文档中也要登记:tcp_server 2000/tcp,公用端口号2000要和服务器相同。
int qid1,qid2,s_c1,s_c2,cport1,cport2; struct servent *sp; struct hostent *hp; memset((char *)&myaddr_in,0,sizeof(struct sockaddr_in)); memset((char *)&peeraddr_in,0,sizeof(struct sockaddr_in)); addrlen=sizeof(struct sockaddr_in); sp=getservbyname("tcp_server","tcp"); hp=gethostbyname(argv[1]); /* 从/etc/hosts中获取服务器的IP地址 */ qid1=msgget(MSGKEY1,0x1ff); qid2=msgget(MSGKEY2,0x1ff); cport1=6000; s=rresvport(&cport1); peeraddr_in.sin_family=hp->h_addrtype; bcopy(hp->h_addr_list[0],(caddr_t)&peeraddr_in.sin_addr,hp->h_length); peeraddr_in.sin_port=sp->s_port; connect(s,(struct sockaddr *)&peeraddr_in,sizeof(peeraddr_in)); cport1--; s_c1=rresvport(&cport1); cport2=cport1; s_c2=rresvport(&cport2); sprintf(cportstr,"%dx%d",cport1,cport2); write(s,cportstr,strlen(cportstr) 1); close(s); |
先给变量cport1置一个整数后调用rresvport函数,该函数先检查端口号cport1是否已被占用,假如已被占用就减一再试,直到找到一个未用的端口号,然后生成一个套接字,将该套接字和端口号相联形成客户机端的半相关,接下调用connect函数向服务器发出连接请求。客户机在发出连接请求之前,已用函数gethostbyname和getservbyname获得了服务器的IP地址及其公用端口号,这样就形成了一个完整的相关,可建立起和服务器的初始连接。接下来再创建两个套接字s_c1和s_c2,利用初始连接将客户机的两个套接字的端口号以字符串的形式发送给服务器,这时初始连接的任务已完成就可将其关闭。以上就完成了和服务器的初始连接,接下来客户机等待服务器的两次连接请求。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇: 在单独线程中执行对象成员函数
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash