迭代服務器比較原始,它的原型可以描述成:
while(1)
{
new_fd = 服務器accept客戶端的連接(new_fd = accept(listenfd, XX, XX))
邏輯處理
在這個new_fd上給客戶端發送消息
關閉new_fd
}
也就是說,這個進程是一個一個處理各個客戶端發來的連接的,比如一個客戶端發來一個連接,那麼只要它還沒有完成自己的任務,那麼它就一直會占用服務器的進程直到處理完畢後服務器關閉掉這個socket。
並發服務器是最經常用的:
1 while(1)
2 {
3 new_fd = 服務器accept客戶端的連接
4 if(是子進程)
5 {
6 首先關閉掉監聽fd // 因為子進程並不需要監聽,它只負責處理邏輯並發消息給客戶端
7 處理邏輯發送消息
8 關閉new_fd
9 關閉進程
10 }
11 關閉new_fd
12 }
這樣每來一個客戶端,服務器就克隆一個自己去處理請求,這樣主進程就一直處於監聽狀態而不會被阻塞
額,我想講的重點不是這裡,重點是代碼第12行!
1。千萬不要以為fork出來一個子進程就產生了2個新的socket描述符,實際上子進程和父進程是共享linten_fd和new_fd的,當父進程關閉new_fd的時候(代碼第12行)它只是把這個new_fd的訪問計數值減了1而已,由於訪問計數值還 > 0(因為還有客戶端的new_fd連著呢),所以它並沒有斷開和客戶端的連接。
2。如果我們不寫第12行會有什麼後果,第一,因為可分配的socket描述符是有限的,如果分配了以後不釋放,也就是不能回收再利用,也就是總有描述符耗盡的一天。第二,本來把和客戶端連接的任務交給子進程以後父進程就可以繼續監聽並accept下個連接了,但如果父進程不關閉自己跟客戶的連接,意思就是這個連接居然永遠存在!相當於每來一個客戶這個父進程就連一個客戶並且連接不斷,你應該知道後果了吧。