本文介紹一種新型的基于消息隊列的重復(fù)型服務(wù)器通信軟件的設(shè)計方法,不同于并發(fā)型服務(wù)器和一般的重復(fù)型服務(wù)器通信軟件,這種新的軟件具有生成的子進程數(shù)少的優(yōu)點,并且容易對客戶機與服務(wù)器的連接進行管理,適用于客戶機數(shù)量較多和隨機數(shù)據(jù)通信的情況,能夠有效地提高服務(wù)器的運行效率。
并發(fā)服務(wù)器與重復(fù)服務(wù)器的區(qū)別
一般TCP/IP服務(wù)器通信軟件都是并發(fā)型的,即是由一個守護進程負(fù)責(zé)監(jiān)聽客戶機的連接請求,然后再由守護進程生成一個或多個子進程與客戶機具體建立連接以完成通信,其缺點是隨著連接的客戶機數(shù)量的增多,生成的通信子進程數(shù)量會越來越多,在客戶機數(shù)量較多的應(yīng)用場合勢必影響服務(wù)器的運行效率。一般的重復(fù)服務(wù)器指的是服務(wù)器在接收客戶機的連接請求后即與之建立連接,然后要在處理完與客戶機的通信任務(wù)后才能再去接收另一客戶機的請求連接,其優(yōu)點是不必生成通信子進程,缺點是客戶機在每次通信之前都要與服務(wù)器建立連接,開銷過大,不能用于隨機的數(shù)據(jù)通信和繁忙的業(yè)務(wù)處理。 來源:www.examda.com
本文提出的新型的重復(fù)型服務(wù)器不同于一般的重復(fù)服務(wù)器,它摒棄了上述兩類服務(wù)器的缺點綜合其優(yōu)點,該服務(wù)器通信軟件具有一般重復(fù)服務(wù)器的特征但又能處理客戶機的隨機訪問,在客戶機數(shù)量多且業(yè)務(wù)繁忙的應(yīng)用場合將發(fā)揮其優(yōu)勢。重復(fù)型服務(wù)器通信軟件只用三個進程就可完成與所有客戶機建立連接,并始終保持這些連接。
重復(fù)型服務(wù)器通信軟件與客戶機建立連接的方法
基本思路
當(dāng)?shù)谝慌_客戶機向服務(wù)器請求連接時,服務(wù)器的守護進程與之建立初始連接(L0),客戶機利用L0向服務(wù)器發(fā)送兩個端口號,守護進程將客戶機的IP地址和端口號登記在共享內(nèi)存的記錄中,然后關(guān)閉L0。由守護進程生成的兩個通信子進程從共享內(nèi)存中獲得客戶機IP地址及端口號后,分別向客戶機請求連接,建立一個從客戶機讀的連接(L1)和一個往客戶機寫的連接(L2),并將兩個連接的套接字的句柄記錄在共享內(nèi)存中。當(dāng)另一臺客戶機請求連接時,守護進程不再生成通信子進程,只是將客戶機IP地址和端口號同樣登記在共享內(nèi)存中。通信子進程在一個大循環(huán)中先查詢共享內(nèi)存中是否有新的記錄,如果有則與這一臺客戶機建立連接,然后輪詢所有已建立的連接的讀套接字,查看是否有數(shù)據(jù)可讀,有則讀取數(shù)據(jù),同時標(biāo)明該數(shù)據(jù)是從共享內(nèi)存中的哪條記錄上的讀套接字中獲得的,再由另一個通信子進程根據(jù)這個記錄的編號從共享內(nèi)存中獲得對應(yīng)的寫套接字,最后將結(jié)果數(shù)據(jù)往該套接字寫往客戶機。 2.2 建立連接
⑴ 服務(wù)器通信軟件的初始進程首先建立公用端口上的套接字,并在該套接字上建立監(jiān)聽隊列,同時生成一個守護進程(Daemon)tcp_s,然后初始進程就退出運行。守護進程在函數(shù)accept處堵塞住直到有客戶機的連接請求,一有連接請求即調(diào)用server函數(shù)處理,然后繼續(xù)循環(huán)等待另一臺客戶機的請求。因為TCP/IP在連接被拆除后為了避免出現(xiàn)重復(fù)連接的現(xiàn)象,一般是將連接放在過時連接表中,連接在拆除后若要避免處于TIME_WAIT狀態(tài)(過時連接),可調(diào)用setsockopt設(shè)置套接字的linger延時標(biāo)志,同時將延時時間設(shè)置為0。服務(wù)器在/etc/services文件中要登記一個全局公認(rèn)的公用端口號: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)聽套接字 */
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); /* 獲得消息隊列的標(biāo)志號 */
qid4=msgget(MSGKEY4,0x1ff);
signal(SIGCLD,SIG_IGN); /* 避免子進程在退出后變?yōu)榻┧肋M程 */
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,"初始進程退出,由守護進程監(jiān)聽客戶機的連接請求.\n");
}
并發(fā)服務(wù)器與重復(fù)服務(wù)器的區(qū)別
一般TCP/IP服務(wù)器通信軟件都是并發(fā)型的,即是由一個守護進程負(fù)責(zé)監(jiān)聽客戶機的連接請求,然后再由守護進程生成一個或多個子進程與客戶機具體建立連接以完成通信,其缺點是隨著連接的客戶機數(shù)量的增多,生成的通信子進程數(shù)量會越來越多,在客戶機數(shù)量較多的應(yīng)用場合勢必影響服務(wù)器的運行效率。一般的重復(fù)服務(wù)器指的是服務(wù)器在接收客戶機的連接請求后即與之建立連接,然后要在處理完與客戶機的通信任務(wù)后才能再去接收另一客戶機的請求連接,其優(yōu)點是不必生成通信子進程,缺點是客戶機在每次通信之前都要與服務(wù)器建立連接,開銷過大,不能用于隨機的數(shù)據(jù)通信和繁忙的業(yè)務(wù)處理。 來源:www.examda.com
本文提出的新型的重復(fù)型服務(wù)器不同于一般的重復(fù)服務(wù)器,它摒棄了上述兩類服務(wù)器的缺點綜合其優(yōu)點,該服務(wù)器通信軟件具有一般重復(fù)服務(wù)器的特征但又能處理客戶機的隨機訪問,在客戶機數(shù)量多且業(yè)務(wù)繁忙的應(yīng)用場合將發(fā)揮其優(yōu)勢。重復(fù)型服務(wù)器通信軟件只用三個進程就可完成與所有客戶機建立連接,并始終保持這些連接。
重復(fù)型服務(wù)器通信軟件與客戶機建立連接的方法
基本思路
當(dāng)?shù)谝慌_客戶機向服務(wù)器請求連接時,服務(wù)器的守護進程與之建立初始連接(L0),客戶機利用L0向服務(wù)器發(fā)送兩個端口號,守護進程將客戶機的IP地址和端口號登記在共享內(nèi)存的記錄中,然后關(guān)閉L0。由守護進程生成的兩個通信子進程從共享內(nèi)存中獲得客戶機IP地址及端口號后,分別向客戶機請求連接,建立一個從客戶機讀的連接(L1)和一個往客戶機寫的連接(L2),并將兩個連接的套接字的句柄記錄在共享內(nèi)存中。當(dāng)另一臺客戶機請求連接時,守護進程不再生成通信子進程,只是將客戶機IP地址和端口號同樣登記在共享內(nèi)存中。通信子進程在一個大循環(huán)中先查詢共享內(nèi)存中是否有新的記錄,如果有則與這一臺客戶機建立連接,然后輪詢所有已建立的連接的讀套接字,查看是否有數(shù)據(jù)可讀,有則讀取數(shù)據(jù),同時標(biāo)明該數(shù)據(jù)是從共享內(nèi)存中的哪條記錄上的讀套接字中獲得的,再由另一個通信子進程根據(jù)這個記錄的編號從共享內(nèi)存中獲得對應(yīng)的寫套接字,最后將結(jié)果數(shù)據(jù)往該套接字寫往客戶機。 2.2 建立連接
⑴ 服務(wù)器通信軟件的初始進程首先建立公用端口上的套接字,并在該套接字上建立監(jiān)聽隊列,同時生成一個守護進程(Daemon)tcp_s,然后初始進程就退出運行。守護進程在函數(shù)accept處堵塞住直到有客戶機的連接請求,一有連接請求即調(diào)用server函數(shù)處理,然后繼續(xù)循環(huán)等待另一臺客戶機的請求。因為TCP/IP在連接被拆除后為了避免出現(xiàn)重復(fù)連接的現(xiàn)象,一般是將連接放在過時連接表中,連接在拆除后若要避免處于TIME_WAIT狀態(tài)(過時連接),可調(diào)用setsockopt設(shè)置套接字的linger延時標(biāo)志,同時將延時時間設(shè)置為0。服務(wù)器在/etc/services文件中要登記一個全局公認(rèn)的公用端口號: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)聽套接字 */
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); /* 獲得消息隊列的標(biāo)志號 */
qid4=msgget(MSGKEY4,0x1ff);
signal(SIGCLD,SIG_IGN); /* 避免子進程在退出后變?yōu)榻┧肋M程 */
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,"初始進程退出,由守護進程監(jiān)聽客戶機的連接請求.\n");
}