客戶 伺服器程式設計方法

2021-09-06 22:21:24 字數 4743 閱讀 6755

示例是乙個經典的tcp回射程式:

客戶端發起連線請求,連線後傳送一串資料;收到服務端的資料後輸出到終端;

服務端收到客戶端的資料後原樣回寫給客戶端;

客戶端偽**:

sockfd = socket(af_inet,sock_stream,0);

//與服務端建立連線

connect(sockfd);

//連線建立後從終端讀入資料併發送到服務端;

//從服務端收到資料後回寫到終端

while(fgets(sendline,maxline,filehandler)!= null)

fputs(recvline,stdout);

}

下面介紹服務端程式處理多個客戶請求的開發正規化;

對於多個客戶請求,伺服器端採用fork的方式建立新程序來處理;

處理流程:

主程序繫結ip埠後,使用accept()等待新客戶的請求;

每乙個新的使用者請求到來,都建立乙個新的子程序來處理具體的客戶請求;

子程序處理完使用者請求,結束本程序;

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

while(true)

}close(connfd);

}

這種方法開發簡單,但對作業系統而言,程序是一種昂貴的資源,對於每個新客戶請求都使用乙個程序處理,開銷較大;

對於客戶請求數不多的應用適用這種方法;

上一種方法中,每來乙個客戶都建立乙個程序處理請求,完畢後再釋放;

不間斷的建立和結束程序浪費系統資源;

使用程序池預先分配程序,通過程序復用,減少程序重複建立帶來的系統消耗和時間等待;

優點:消除新客戶請求到達來建立程序的開銷;

缺點:需要預先估算客戶請求的多少(確定程序池的大小)

源自berkeley核心的系統,有以下特性:

派生的所有子程序各自呼叫accep()監聽同乙個套接字,在沒有使用者請求時都進入睡眠;

當有新客戶請求到來時,所有的客戶都被喚醒;核心從中選擇乙個程序處理請求,剩餘的程序再次轉入睡眠(回到程序池);

利用這個特性可以由作業系統來控制程序的分配;

核心排程演算法會把各個連線請求均勻的分散到各個程序中;

處理流程:

主程序預先分配程序池,所有子程序阻塞在accept()呼叫上;

新使用者請求到來,作業系統喚醒所有的阻塞在accpet上的程序,從其中選擇乙個建立連線;

被選中的子程序處理使用者請求,其它子程序回到睡眠;

子程序處理完畢,再次阻塞在accept上;

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

for(int i = 0;i< children;i++)

close(connfd);}}

}

如何從程序池中取出程序?

所有的程序都通過accept()阻塞等待,等連線請求到來後,由核心從所有等待的程序中選擇乙個程序處理;

處理完的程序,如何放回到池子中?

子程序處理完客戶請求後,通過無限迴圈,再次阻塞在accpet()上等待新的連線請求;

注意:多個程序accept()阻塞會產生「驚群問題」:儘管只有乙個程序將獲得連線,但是所有的程序都被喚醒;這種每次有乙個連線準備好卻喚醒太多程序的做法會導致效能受損;

上述不上鎖的實現存在移植性的問題(只能在源自berkeley的核心系統上)和驚群問題,

更為通用的做法是對accept上鎖;即避免讓多個程序阻塞在accpet呼叫上,而是都阻塞在獲取鎖的函式中;

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

for(int i = 0;i< children;i++)

close(connfd);}}

}

上鎖可以使用檔案上鎖,執行緒上鎖;

關於上鎖的編碼細節詳見《網路程式設計》第30章;

與上面的每個程序各自accept接收監聽請求不同,這個方法是在父程序中統一接收accpet()使用者請求,在連線建立後,將連線描述符傳遞給子程序;

處理流程:

主程序阻塞在accpet上等待使用者請求,所有子程序不斷輪詢探查是否有可用的描述符;

有新使用者請求到來,主程序accpet建立連線後,從程序池中取出乙個程序,通過位元組流管道將連線描述符傳遞給子程序;

子程序收到連線描述符,處理使用者請求,處理完成後向父程序傳送乙個位元組的內容(無實際意義),告知父程序我任務已完成;

父程序收到子程序的單位元組資料,將子程序放回到程序池;

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

//預先建立子程序池

for(int i = 0;i< children;i++)

while(n=read(connfd,buf,maxline)>0)

close(connfd);

//通知父程序處理完畢,本程序可以回到程序池

write(stderr_fileno,"",1);}}

}while(true)

child_status[i] = 1;//子程序從程序池中分配出去

write_fd(childfd[i],connfd);//將描述符傳遞到子程序中

close(connfd);

}//檢查子程序的描述符,有資料,表明已經子程序請求已處理完成,**到程序池

for(int i = 0 ;i < children;i++)}}

}

為每個使用者建立乙個執行緒,這種方法比為每個使用者建立乙個程序要快出許多倍;

處理流程:

主線程阻塞在accpet上等待用請求;

有新使用者請求時,主線程建立連線,然後建立乙個新的執行緒,將連線描述符傳遞過去;

子執行緒處理使用者請求,完畢後執行緒結束;

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

while(true)

--------------------

//具體的使用者請求處理函式(子執行緒主體)

void * do_function(void * connfd)

處理流程:

使用者請求到來,第乙個子執行緒建立連線後釋放鎖,然後處理使用者請求;完成後進入執行緒池,等待獲取鎖;

第乙個子執行緒釋放鎖之後,執行緒池中等待的執行緒有乙個會獲取到鎖,阻塞在accept()等待使用者請求;

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

//預先建立執行緒池,將監聽描述符傳給每個新建立的執行緒

for(int i = 0 ;i 0)

}

這裡沒有必要使用檔案上鎖,因為單個程序中的多個執行緒,總是可以通過執行緒互斥鎖來達到同樣目的;(檔案鎖更慢)

處理流程:

主線程預先建立執行緒池,執行緒池中所有的執行緒都通過呼叫pthread_cond_wait()而處於睡眠狀態(由於有鎖的保證,是依次進入睡眠,而不會發生同時呼叫pthread_cond_wait引發競爭)

主線程阻塞在acppet呼叫上等待使用者請求;

使用者請求到來,主線程accpet建立建立,將連線控制代碼放入約定位置後,傳送pthread_cond_signal啟用乙個等待該條件的執行緒;

執行緒啟用後從約定位置取出連線控制代碼處理使用者請求;完畢後再次進入睡眠(回到執行緒池);

啟用條件等待的方式有兩種:pthread_cond_signal()啟用乙個等待該條件的執行緒,存在多個等待執行緒時按入隊順序啟用其中乙個;而pthread_cond_broadcast()則啟用所有等待執行緒。

服務端偽**:

listenfd = socket(af_inet,sock_stream,0);

bind(listenfd,addr);

listen(listenfd);

for(int i = 0 ;i 0)

}

測試表明這個版本的伺服器要慢於每個執行緒各自accpet的版本,原因在於這個版本同時需要互斥鎖和條件變數,而上乙個版本只需要互斥鎖;

執行緒描述符的傳遞和程序描述符的傳遞的區別?

在乙個程序中開啟的描述符對該程序中的所有執行緒都是可見的,引用計數也就是1;

所有執行緒訪問這個描述符都只需要通過乙個描述符的值(整型)訪問;

而程序間的描述符傳遞,傳遞的是描述符的引用;(好比乙個檔案被2個程序開啟,相應的這個檔案的描述符引用計數增加2);

《unix網路程式設計》第一卷 套接字聯網api

客戶伺服器程式設計示例

先建立兩個工程server 和client 新增stdafx.h 和winsock2.h 兩個標頭檔案,然後編寫下面的兩段 server.cpp include stdafx.h include winsock2.h include define tchar char pragma comment ...

客戶 伺服器程式設計正規化

本篇從基於tcp ip協議出發,現代流行的應對高併發請求網路服務端設計架構 1.tcp ip 模型 首先回顧一下tcp ip模型,並知道各個層次在作業系統的哪乙個層次 看上圖,osi模型的底下兩層是隨系統提供的裝置驅動程式和網路硬體。通常情況下,除需知道資料鏈路的某些特性外,我們不用關心這兩層的情況...

客戶 伺服器程式設計正規化

unix 網路程式設計第30章讀書筆記,這裡只記錄大致實現方式,具體 實現還請閱讀此書 tcp 迭代伺服器 完全同步方式,完全處理某個客戶的請求之後才專向下乙個客戶,優點是 簡單,並且沒有程序控制所需的時間 tcp 併發伺服器程式,每個客戶乙個子程序 傳統上併發伺服器呼叫fork 派生乙個子程序來處...