《UNIX網路程式設計卷1》讀書筆記--第五章TCP客戶/服務例項
阿新 • • 發佈:2019-01-29
前言
本章開始編寫一個完整的TCP客戶/伺服器程式例項。
(1) 客戶衝標準輸入讀入一行文字,並寫給伺服器
(2)伺服器從網路輸入讀入這行文字,並回射給客戶
(3)客戶從網路讀入這行回射文字,並顯示在標準輸出上。
(這個圖是根據書中原圖我重新繪製的圖,可以看到伺服器呼叫來了read而客戶端呼叫readline,但是根據之前的講訴,readline能夠保證一次讀取“全部”資訊,還有書中後面關於str_echo函式的描述,我認為這裡TCP伺服器應該使用readline更加合理,儘管程式碼和圖中給出的是read呼叫)
關注點
- 客戶和伺服器正常啟動
- 防止殭屍程序的訊號處理
- 伺服器程序在客戶之前終止,客戶端情況
- 伺服器主機崩潰,客戶端情況
- 伺服器主機崩潰後重啟,客戶端情況
客戶和伺服器正常啟動與終止
如圖所示,正常啟動後客戶端阻塞於fgets呼叫,伺服器子程序阻塞與readline(read)呼叫,伺服器父程序阻塞於accept呼叫。因此三個程序都處於睡眠狀態。
正常終止:
防止殭屍程序的訊號處理
如果父程序沒有捕獲子程序的SIGCHID訊號,那麼子程序將成為殭屍程序。
如何捕獲?可以採用wait或者waitpid
#include <sys/wait.h>
pit_t wait(int *statloc);
pid_t waitpid (pid_t pid, in * staloc, int options);
當有父程序fork出多個子程序時,應該採用waitpid獲得全部子程序pid來進行訊號捕獲。呼叫wait將產生不確定的後果。
遺留問題
伺服器程序在客戶之前終止,客戶端情況
這裡說的終止是區別於主機崩潰的終止,也就是說,終止前是有給客戶端傳送FIN的,伺服器主機關機也跟這類終止相同。 但是雖然傳送的FIN可以得到客戶TCP的ACK回覆,但是正阻塞在fgets呼叫的客戶端程式卻沒有察覺,如果此時客戶端輸入並且將資料傳送給對端,這時服務端會回覆RST,為什麼?因為這個和一般的終止連線不同,對端程序已經終止,沒有程序能識別這個資料。 此時客戶呼叫readline讀取到剛開始的FIN,這時readline返回0,報告連線終止。 連線已經終止,但是如果我們此時沒有終止程序,這時可能的,我們再繼續往不存在的連線傳送資料就會產生SIGPIPE訊號(核心嚮應用程序傳送),該訊號的預設行為是終止程序,因此程序必須捕獲它以免不情願地被終止。
伺服器主機崩潰,客戶端情況
伺服器主機崩潰,那麼使用者傳送的資料將使得路由器相應一個destination unreachable的ICMP訊息。客戶端將會產生數次重傳。處理這種問題可以有兩種方法:
1.對readline呼叫設定一個更短的超時
2.採用SO_KEEPALIVE選項
伺服器主機崩潰後重啟,客戶端情況
伺服器重啟後,它的TCP丟失了崩潰前的所有連線訊息,因此伺服器TCP對於所有收到的來自客戶的資料分節響應一個RST。而此時客戶正阻塞在readline呼叫,導致該呼叫返回ECONNRESET