陳春嬌
摘要:該文比較詳細地介紹了循環(huán)和并發(fā)服務器的概念、工作原理及循環(huán)和并發(fā)服務器的算法,從工作原理、工作流程等方面進行闡述兩者的區(qū)別,并分別用一個循環(huán)服務器程序和并發(fā)服務器程序實例代碼加以說明。
關鍵詞:循環(huán)服務器;并發(fā)服務器;算法;應用
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2012)32-7706-04
1循環(huán)和并發(fā)服務器的概念
網(wǎng)絡服務器有循環(huán)服務器和并發(fā)服務器兩種。循環(huán)服務器:循環(huán)服務器在同一個時刻只可以響應一個客戶端的請求;并發(fā)服務器:并發(fā)服務器在同一個時刻可以響應多個客戶端的請求。
網(wǎng)絡服務器有循環(huán)服務器和并發(fā)服務器兩種。連接性問題是傳輸協(xié)議的中心,而客戶使用這個傳輸協(xié)議訪問某個服務器。TCP/IP協(xié)議族給用提供了兩種傳輸協(xié)議,可以使用面向連接的傳輸(TCP)或無連接的傳輸(UDP)。因此,可以將服務器劃分為四種一般的類型:循環(huán)無連接、循環(huán)面向連接、并發(fā)無連接、并發(fā)面向連接。
2循環(huán)和并發(fā)服務的工作原理
2.1循環(huán)服務器的工作原理及及流程
1)循環(huán)無連接(UDP)的服務器的工作原理及應用范圍
循環(huán)無連接的服務器的算法
①創(chuàng)建套接字并將其綁定到所提供服務的熟知端口上。
②重復地讀取來自客戶的請求,構造響應,按照應用協(xié)議向客戶發(fā)回響應。
UDP循環(huán)服務器的實現(xiàn)非常簡單:UDP服務器每次從套接字上讀取一個客戶端的請求,處理,然后將結果返回給客戶機.
可以用下面的算法來實現(xiàn).
socket(...);
bind(...);
while(1)
{
recvfrom(...);
process(...);
sendto(...);
}
因為UDP是非面向連接的,沒有一個客戶端可以老是占住服務端.只要處理過程不是死循環(huán),服務器對于每一個客戶機的請求總是能夠滿足.
2)循環(huán)面向連接(TCP)的服務器的工作原理及應用范圍
循環(huán)面向連接的服務器的算法
①創(chuàng)建套接字并將其綁定到它所提供服務的熟知端口上。
②將該端口設置為被動模式,使其準備為服務器所用。
③從該套接字上接受下一個連接請求,獲得該連接的新的套接字。
④重復地讀取來自客戶的請求,構造響應,按照應用協(xié)議向客戶發(fā)回響應。
⑤當與某個特定客戶完成交互時,關閉連接,并返回步驟3以接受新的連接。
TCP循環(huán)服務器的實現(xiàn)也不難:TCP服務器接受一個客戶端的連接,然后處理,完成了這個客戶的所有請求后,斷開連接。
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
}
TCP循環(huán)服務器一次只能處理一個客戶端的請求.只有在這個客戶的所有請求都滿足后,服務器才可以繼續(xù)后面的請求.這樣如果有一個客戶端占住服務器不放時,其它的客戶機都不能工作了.因此,TCP服務器一般很少用循環(huán)服務器模型的.
2.2并發(fā)服務器的工作原理及應用范圍
1)并發(fā)面向連接(TCP)的服務器的工作原理及應用范圍
并發(fā)服務器:TCP服務器
為了彌補循環(huán)TCP服務器的缺陷,人們又想出了并發(fā)服務器的模型.并發(fā)服務器的思想是每一個客戶機的請求并不由服務器直接處理,而是服務器創(chuàng)建一個子進程來處理.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
if(fork(..)==0)
{
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
exit(...);
}
close(...);
}
TCP并發(fā)服務器可以解決TCP循環(huán)服務器客戶機獨占服務器的情況.不過也同時帶來了一個不小的問題.為了響應客戶機的請求,服務器要創(chuàng)建子進程來處理.而創(chuàng)建子進程是一種非常消耗資源的操作.
2)并發(fā)無連接的服務器的工作原理及應用范圍
不同于順序服務器,并發(fā)服務器就要能在一個時間為多個客戶端提供服務。例如,一個聊天服務器可能服務一個特定的客戶端數(shù)小時──在停止為這個客戶端服務之前服務器不能等待,除非是在等待一下個客戶端到來之前的間隙才能等待。
我們將提供服務從守護進程移至它自己的服務進程。然而,因為每個子進程都繼承所有打開的文件(套接字被像文件一樣處理),新進程不僅繼承“accept()返回的句柄,”那是指調用accept返回的套接字;新進程也繼承頂級套接字,這是頂級進程一開始打開的套接字。
然而,服務進程不需要這個套接字,應該立即關閉(close)它。同樣的,守護進程不再需要accept()返回的套接字,不僅應該,還必須關閉(close)它──否則,那遲早會耗盡可用的文件描述符。
在服務進程完成服務之后,它將關閉accept()返回的套接字。它不會返回到accept,而是退出進程。
在UNIX?上,一個進程并不真正的退出,而是返回至父進程。典型情況中,父進程等待(wait)子進程,并取得一個返回值。但是,我們的守護進程不能簡單的停止或等待,那有違建立其它進程的整個目的。但是如果從不使用wait,它的子進程可能會成為僵尸──不再有功用可仍然徘徊著。
出于那樣的原因,守護進程需要在初始化守護進程階段設置信號處理程序。至少要處理信號SIGCHLD,這樣守護進程可以從系統(tǒng)清除僵尸返回值并釋放僵尸占用的系統(tǒng)資源。
這是現(xiàn)在我們的流程圖包含一個進程信號框的原因,它不與任何其它框相連接。順便說一句,許多服務器程序也處理SIGHUP,作為超級用戶發(fā)出的要求重讀配置文件的信號。這允許我們不必終止或重啟服務器程序就改變設置。
3循環(huán)服務器和并發(fā)服務器的比較及應用場景
3.1并發(fā)服務器和循環(huán)服務器比較
一般來說,并發(fā)服務器更難設計和構建,其最終的代碼也更復雜并且難于修改。然而,大多數(shù)程序員還是選擇了并發(fā)實現(xiàn)的方法,因為循環(huán)服務器會在分布式應用中引起不必要的時延,而且可能會成為影響許多客戶應用程序的性能瓶頸。我們概括如下:使用循環(huán)方法實現(xiàn)的服務器易于構建和理解,但這樣的結果會使其性能很差,因為這樣的服務器要使客戶等待服務。相反,以并發(fā)方法實現(xiàn)的服務器難于設計和構建,但卻有較好的性能。
3.2循環(huán)服務器和并發(fā)服務器的應用場景
1)循環(huán)的和并發(fā)的:如果循環(huán)方案產(chǎn)生的響應時間對應用來說足夠充分,就可以使用循環(huán);否則需要并發(fā);2)真正的和表面上的并發(fā)性:如果線程或切換環(huán)境的開銷大,服務器需要在多個連接之間共享或者交換數(shù)據(jù),用單線程;使用線程開銷不大或者要得到最大并發(fā)性,使用多進程;3)面向連接的和無連接的:如果應用協(xié)議處理了可靠性問題,或者應用在局域網(wǎng)環(huán)境內(nèi),使用無連接的傳輸。
4循環(huán)和并發(fā)服務器程序實例
循環(huán)服務器容易設計、實現(xiàn)和維護,但是性能差;并發(fā)服務器難以構建和設計,但是性能好,響應快。
4.1循環(huán)服務器的應用程序實例:
循環(huán)式服務器模型:(以時間服務程序time為例)
1)服務器端主函數(shù)部分代碼分析
①前期準備部分
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,表示除了程序名外還有一個參數(shù)。argv[0]指向輸入的程序路徑及名稱。argv[1]指向參數(shù)的字符串;Default報錯。*/
sock=passiveUDP(service);
/*調用已定義的passiveUDP函數(shù)創(chuàng)建套接字*/
②實際處理連接通信服務部分
while(1){
alen=sizeof(fsin);
if(recvfrom(sock,buf,sizeof(buf),0,(structsockaddr*)&fsin,&alen)<0)
errexit("recvfrom:%s\n",strerror(errno));
/*讀取來自客戶端發(fā)起的請求,若返回值小于0就讀取失敗調用errexit函數(shù)報錯*/
(void)time(&now); /*獲取時間*/
now=htonl((unsignedlong)(now+UNIXEPOCH));
/*將本地字節(jié)順序轉換成客戶端能識別的統(tǒng)一的網(wǎng)絡字節(jié)順序*/
(void)sendto(sock,(char*)&now,sizeof(now),0,(structsockaddr*)&fsin,sizeof(fsin));
/*調用sendto函數(shù)將時間信息發(fā)送給客戶端*/
}
③ passivesock函數(shù)模塊關鍵代碼分析:(含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本地字節(jié)順序與網(wǎng)絡字節(jié)順序的相互轉換;設置全局的portbase可以重新分配端口值,映射到更高的端口,這樣測試就不會與原來的重復,還能運行原來的服務的,便于測試*/
s=socket(PF_INET,type,ppe→p_proto);
if(s<0)
errexit("can'tcreatesocket:%s\n",strerror(errno));
/*創(chuàng)建套接字,成功則返回描述符S,失敗則報錯*/
bind(s,(structsockaddr*)&sin,sizeof(sin)) /*綁定端口地址*/
listen(s,qlen) /*調用listen函數(shù)設為監(jiān)聽狀態(tài)*/
5結束語
該文共分為四個部分,第一部分比較詳細地介紹了循環(huán)和并發(fā)服務器的概念;第二部分詳細介紹循環(huán)和并發(fā)服務器的工作原理及算法,第三部分主要講循環(huán)和并發(fā)服務器比較及應用場合,第四部分是舉出循環(huán)服務器和并發(fā)服器的程序實例代碼并進行分析。在撰寫這篇論文過程中,明顯感覺該學科的的難度,但正是在這個摸索的過程中,通過實踐對該課程的知識有了更深層的理解。
參考文獻:
[1]ComerDE,StevensDL.用TCP/IP進行網(wǎng)際互連[M].趙剛,林瑤,蔣慧,譯.北京:電子工業(yè)出版社,2011.
[2]杜煜.計算機網(wǎng)絡基礎[M].北京:人民郵電出版社,2002.
[3]RameshS,PerrorsHG.Amultilayerclient-serverqueueingnetworkmodelwithsynchronousandasynchronousmessages[J].IEEETransactiononSoftwareEngineering,2000,26(11):1086-1100.
[4]洪英.Linux中帳號的安全管理方法[J].科技創(chuàng)新導報,2008(4):12.
[5]關于main函數(shù)的(int_argc,char_argv[]).51CTOLinux編程學習[Z].
[6]武奇生.網(wǎng)絡與TCP/IP協(xié)議[M].西安:西安電子科大出版社,2010.