1. 程式人生 > >《UNIX網路程式設計卷1》讀書筆記--第五章TCP客戶/服務例項

《UNIX網路程式設計卷1》讀書筆記--第五章TCP客戶/服務例項

前言

  本章開始編寫一個完整的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