- 相關(guān)推薦
TCP/IP網(wǎng)絡(luò)重復(fù)型服務(wù)器通信軟件設(shè)計(jì)
摘要:本文介紹一種新型的基于消息隊(duì)列的重復(fù)型服務(wù)器通信軟件的設(shè)計(jì)方法,不同于并發(fā)型服務(wù)器和一般的重復(fù)型服務(wù)器通信軟件,這種新的軟件具有生成的子進(jìn)程數(shù)少的優(yōu)點(diǎn),并且容易對(duì)客戶機(jī)與服務(wù)器的連接進(jìn)行管理,適用于客戶機(jī)數(shù)量較多和隨機(jī)數(shù)據(jù)通信的情況,能夠有效地提高服務(wù)器的運(yùn)行效率。
關(guān)鍵詞:TCP/IP網(wǎng)絡(luò) 重復(fù)型服務(wù)器通信軟件 套接字 連接 共享內(nèi)存 消息隊(duì)列
并發(fā)服務(wù)器與重復(fù)服務(wù)器的區(qū)別
一般TCP/IP服務(wù)器通信軟件都是并發(fā)型的,即是由一個(gè)守護(hù)進(jìn)程負(fù)責(zé)監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求,然后再由守護(hù)進(jìn)程生成一個(gè)或多個(gè)子進(jìn)程與客戶機(jī)具體建立連接以完成通信,其缺點(diǎn)是隨著連接的客戶機(jī)數(shù)量的增多,生成的通信子進(jìn)程數(shù)量會(huì)越來(lái)越多,在客戶機(jī)數(shù)量較多的應(yīng)用場(chǎng)合勢(shì)必影響服務(wù)器的運(yùn)行效率。一般的重復(fù)服務(wù)器指的是服務(wù)器在接收客戶機(jī)的連接請(qǐng)求后即與之建立連接,然后要在處理完與客戶機(jī)的通信任務(wù)后才能再去接收另一客戶機(jī)的請(qǐng)求連接,其優(yōu)點(diǎn)是不必生成通信子進(jìn)程,缺點(diǎn)是客戶機(jī)在每次通信之前都要與服務(wù)器建立連接,開(kāi)銷過(guò)大,不能用于隨機(jī)的數(shù)據(jù)通信和繁忙的業(yè)務(wù)處理。
本文提出的新型的重復(fù)型服務(wù)器不同于一般的重復(fù)服務(wù)器,它摒棄了上述兩類服務(wù)器的缺點(diǎn)綜合其優(yōu)點(diǎn),該服務(wù)器通信軟件具有一般重復(fù)服務(wù)器的特征但又能處理客戶機(jī)的隨機(jī)訪問(wèn),在客戶機(jī)數(shù)量多且業(yè)務(wù)繁忙的應(yīng)用場(chǎng)合將發(fā)揮其優(yōu)勢(shì)。重復(fù)型服務(wù)器通信軟件只用三個(gè)進(jìn)程就可完成與所有客戶機(jī)建立連接,并始終保持這些連接。
重復(fù)型服務(wù)器通信軟件與客戶機(jī)建立連接的方法
基本思路
當(dāng)?shù)谝慌_(tái)客戶機(jī)向服務(wù)器請(qǐng)求連接時(shí),服務(wù)器的守護(hù)進(jìn)程與之建立初始連接(L0),客戶機(jī)利用L0向服務(wù)器發(fā)送兩個(gè)端口號(hào),守護(hù)進(jìn)程將客戶機(jī)的IP地址和端口號(hào)登記在共享內(nèi)存的記錄中,然后關(guān)閉L0。由守護(hù)進(jìn)程生成的兩個(gè)通信子進(jìn)程從共享內(nèi)存中獲得客戶機(jī)IP地址及端口號(hào)后,分別向客戶機(jī)請(qǐng)求連接,建立一個(gè)從客戶機(jī)讀的連接(L1)和一個(gè)往客戶機(jī)寫(xiě)的連接(L2),并將兩個(gè)連接的套接字的句柄記錄在共享內(nèi)存中。當(dāng)另一臺(tái)客戶機(jī)請(qǐng)求連接時(shí),守護(hù)進(jìn)程不再生成通信子進(jìn)程,只是將客戶機(jī)IP地址和端口號(hào)同樣登記在共享內(nèi)存中。通信子進(jìn)程在一個(gè)大循環(huán)中先查詢共享內(nèi)存中是否有新的記錄,如果有則與這一臺(tái)客戶機(jī)建立連接,然后輪詢所有已建立的連接的讀套接字,查看是否有數(shù)據(jù)可讀,有則讀取數(shù)據(jù),同時(shí)標(biāo)明該數(shù)據(jù)是從共享內(nèi)存中的哪條記錄上的讀套接字中獲得的,再由另一個(gè)通信子進(jìn)程根據(jù)這個(gè)記錄的編號(hào)從共享內(nèi)存中獲得對(duì)應(yīng)的寫(xiě)套接字,最后將結(jié)果數(shù)據(jù)往該套接字寫(xiě)往客戶機(jī)。
建立連接
、 服務(wù)器通信軟件的初始進(jìn)程首先建立公用端口上的套接字,并在該套接字上建立監(jiān)聽(tīng)隊(duì)列,同時(shí)生成一個(gè)守護(hù)進(jìn)程(Daemon)tcp_s,然后初始進(jìn)程就退出運(yùn)行。守護(hù)進(jìn)程在函數(shù)accept處堵塞住直到有客戶機(jī)的連接請(qǐng)求,一有連接請(qǐng)求即調(diào)用server函數(shù)處理,然后繼續(xù)循環(huán)等待另一臺(tái)客戶機(jī)的請(qǐng)求。因?yàn)門(mén)CP/IP在連接被拆除后為了避免出現(xiàn)重復(fù)連接的現(xiàn)象,一般是將連接放在過(guò)時(shí)連接表中,連接在拆除后若要避免處于TIME_WAIT狀態(tài)(過(guò)時(shí)連接),可調(diào)用setsockopt設(shè)置套接字的linger延時(shí)標(biāo)志,同時(shí)將延時(shí)時(shí)間設(shè)置為0。服務(wù)器在/etc/services文件中要登記一個(gè)全局公認(rèn)的公用端口號(hào):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); /* 創(chuàng)建監(jiān)聽(tīng)套接字 */ myaddr_in.sin_addr.s_addr=INADDR_ANY; myaddr_in.sin_port=sp->s_port; /* 公用端口號(hào) */ bind(ls,&myaddr_in,sizeof(struct sockaddr_in)); listen(ls,5); qid3=msgget(MSGKEY3,0x1ff); /* 獲得消息隊(duì)列的標(biāo)志號(hào) */ qid4=msgget(MSGKEY4,0x1ff); signal(SIGCLD,SIG_IGN); /* 避免子進(jìn)程在退出后變?yōu)榻┧肋M(jìn)程 */ 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,"初始進(jìn)程退出,由守護(hù)進(jìn)程監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求.\n"); } |
、 客戶機(jī)以這樣的形式運(yùn)行通信程序tcp_c:tcp_c rhostname,rhostname為客戶機(jī)所要連接的服務(wù)器主機(jī)名?蛻魴C(jī)上的/etc/services文件中也要登記:tcp_server 2000/tcp,公用端口號(hào)2000要與服務(wù)器一樣。
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中獲取服務(wù)器的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置一個(gè)整數(shù)后調(diào)用rresvport函數(shù),該函數(shù)先檢查端口號(hào)cport1是否已被占用,如果已被占用就減一再試,直到找到一個(gè)未用的端口號(hào),然后生成一個(gè)套接字,將該套接字與端口號(hào)相聯(lián)形成客戶機(jī)端的半相關(guān),接下調(diào)用connect函數(shù)向服務(wù)器發(fā)出連接請(qǐng)求。客戶機(jī)在發(fā)出連接請(qǐng)求之前,已用函數(shù)gethostbyname和getservbyname獲得了服務(wù)器的IP地址及其公用端口號(hào),這樣就形成了一個(gè)完整的相關(guān),可建立起與服務(wù)器的初始連接。接下來(lái)再創(chuàng)建兩個(gè)套接字s_c1和s_c2,利用初始連接將客戶機(jī)的兩個(gè)套接字的端口號(hào)以字符串的形式發(fā)送給服務(wù)器,這時(shí)初始連接的任務(wù)已經(jīng)完成就可將其關(guān)閉。以上就完成了與服務(wù)器的初始連接,接下來(lái)客戶機(jī)等待服務(wù)器的兩次連接請(qǐng)求。
【TCP/IP網(wǎng)絡(luò)重復(fù)型服務(wù)器通信軟件設(shè)計(jì)】相關(guān)文章:
TCP/IP網(wǎng)絡(luò)協(xié)議知識(shí)點(diǎn)的歸納09-02
2015計(jì)算機(jī)三級(jí)網(wǎng)絡(luò)技術(shù)考試重點(diǎn):TCP/IP09-02
2016四級(jí)網(wǎng)絡(luò)工程師學(xué)習(xí)筆記:TCP/IP聯(lián)網(wǎng)08-04
2015計(jì)算機(jī)四級(jí)網(wǎng)絡(luò)工程師考前輔導(dǎo):TCP/IP的分層11-10
計(jì)算機(jī)四級(jí)考試網(wǎng)絡(luò)工程師考點(diǎn):TCP/IP聯(lián)網(wǎng)06-09
計(jì)算機(jī)等考三級(jí)網(wǎng)絡(luò)技術(shù)考點(diǎn):TCP/IP參考模型與協(xié)議11-08
2016年計(jì)算機(jī)四級(jí)網(wǎng)絡(luò)工程師筆記:TCP/IP的分層10-20
php通過(guò)記錄IP來(lái)防止表單重復(fù)提交方法分析10-17
計(jì)算機(jī)四級(jí)復(fù)習(xí)知識(shí)點(diǎn):TCP/IP聯(lián)網(wǎng)06-17