併發服務器和循環服務器

服務器模型

在網絡程序裏面,一般來說都是許多客戶對應一個服務器,爲了處理客戶的請求, 對服務端的程序就提出了特殊的要求。目前最常用的服務器模型有:
• 循環服務器:服務器在同一個時刻只可以響應一個客戶端的請求
• 併發服務器:服務器在同一個時刻可以響應多個客戶端的請求


UDP循環服務器


UDP循環服務器的實現方法:UDP服務器每次從套接字上讀取一個客戶端的請求->處理->然後將結果返回給客戶機。
socket(...);
bind(...);
while(1)
{
recvfrom(...);
process(...);
sendto(...);
}
因爲UDP是非面向連接的,沒有一個客戶端可以老是佔住服務端, 服務器對於每一個客戶機的請求總是能夠滿足。


TCP循環服務器


TCP服務器接受一個客戶端的連接,然後處理,完成了這個客戶的所有請求後,斷開連接。算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
process(...);
close(...);
}


TCP循環服務器一次只能處理一個客戶端的請求。只有在這個客戶的所有請求都滿足後, 服務器纔可以繼續後面的請求。這樣如果有一個客戶端佔住服務器不放時,其它的客戶機都不能工作了,因此,TCP服務器一般很少用循環服務器模型的。


TCP併發服務器


併發服務器的思想是每一個客戶機的請求並不由服務器直接處理,而是由服務器創建一個子進程來處理。算法如下:
socket(...);
bind(...);
listen(...);
while(1) {
accept(...);
if(fork(..)==0) {
process(...);
close(...);
exit(...);
}
close(...);
}


TCP併發服務器可以解決TCP循環服務器客戶機獨佔服務器的情況。但同時也帶來了問題:爲了響應客戶的請求,服務器要創建子進程來處理,而創建子進程是一種非常消耗資源的操作。


多路複用I/O


阻塞函數在完成其指定的任務以前不允許程序繼續向下執行。例如:當服務器運行到accept語句時,而沒有客戶請求連接,服務器就會停止在accept語句上等待連接請求的到來。這種情況稱爲阻塞(blocking),而非阻塞操作則可以立即完成。例如,如果你希望服務器僅僅檢查是否有客戶在等待連接,有就接受連接,否則就繼續做其他事情,則可以通過使用select系統調用來實現。除此之外,select還可以同時監視多個套接字。


int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set
*exceptfds, const struct timeval *timeout)
Maxfd: 文件描述符的範圍,比待檢的最大文件描述符大1
Readfds:被讀監控的文件描述符集
Writefds:被寫監控的文件描述符集
Exceptfds:被異常監控的文件描述符集
Timeout:定時器


Timeout取不同的值,該調用有不同的表現:
Timeout值爲0,不管是否有文件滿足要求,都立刻返回,無文件滿足要求返回0,有文件滿足要求返回一個正值。
Timeout爲NULL,select將阻塞進程,直到某個文件滿足要求
Timeout值爲正整數,就是等待的最長時間,即
select在timeout時間內阻塞進程。


Select調用返回時,返回值有如下情況:
1.正常情況下返回滿足要求的文件描述符個數;
2.經過了timeout等待後仍無文件滿足要求,返回值爲0;
3.如果select被某個信號中斷,它將返回-1並設置errno爲EINTR。
4.如果出錯,返回-1並設置相應的errno。


1. 設置要監控的文件
2. 調用Select開始監控
3. 判斷文件是否發生變化


系統提供了4個宏對描述符集進行操作:
#include
void FD_SET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET將文件描述符fd添加到文件描述符集fdset中;
宏FD_CLR從文件描述符集fdset中清除文件描述符fd;
宏FD_ZERO清空文件描述符集fdset;
在調用select後使用FD_ISSET來檢測文件描述符集fdset中的文件fd發生了變化。


FD_ZERO(&fds); //清空集合
sock1 = socket(……);
sock2 = socket(……);
bind(sock1,…);
bind(sock2,…);
listen(sock1,…);
listen(sock1,…);
FD_SET(sock1,&fds); //設置描述符
FD_SET(sock2,&fds); //設置描述符
maxfdp=(sock1>sock2?sock1:sock2) + 1;
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select錯誤,退出程序
case 0:break;
default:
if(FD_ISSET(sock1,&fds)) //測試sock1是否可讀
accpet(sock1,…)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章