概述
本章主要實現(xiàn)的程序模型:
2 TCP回射服務器程序
服務器與客戶程序約定一個固定的端口策泣,要比5000大彰阴,比49152小吗垮。
fork后子進程第一件事就是關掉listenfd问裕,父進程的第一件事是關掉connfd掀虎。
在等待客戶的read調用返回出錯后,如果是因為被信號打斷狸剃,要重新調用read掐隐。
正常情況
正常啟動
監(jiān)聽套接字處于LISTEN狀態(tài)。
客戶的connect在三路握手的第二個分節(jié)就返回了钞馁,而服務器要直到第三個分節(jié)才返回虑省,即客戶的connect返回時服務器還沒有accept。
服務器的連接套接字處于ESTABBLISHED狀態(tài)僧凰。
正常終止
客戶端主動關閉時探颈,客戶TCP發(fā)送一個FIN給服務器,服務器TCP響應ACK训措,此時服務器處于CLOSE_WAIT狀態(tài)伪节,客戶處于FIN_WAIT_2狀態(tài)光羞。
服務器關閉時,服務器發(fā)送FIN給客戶怀大,客戶發(fā)送ACK給服務器纱兑,連接完全終止,客戶進入TIME_WAIT狀態(tài)化借。
服務器子進程終止時給父進程發(fā)送一個SIGCHLD信號潜慎。默認行為是忽略,但我們必須捕捉此信號蓖康,清理僵死進程铐炫。
POSIX信號處理
信號是某個進程發(fā)生了某個事件的通知,有時也稱為軟件中斷蒜焊,通常是異步發(fā)生的驳遵,也就是說進程預先不知道信號的準確發(fā)生時刻。
信號可以:
由一個進程發(fā)給另一個進程
由內(nèi)核發(fā)給某個進程
每個信號都有一個與之關聯(lián)的處置也稱行為山涡。
處理SIGCHLD信號
多進程下父進程必須捕捉SIGCHLD信號以回收終止狀態(tài)的子進程資源,否則進程處于僵尸狀態(tài)∷羟ǎ可以在信號處理函數(shù)中用wait或waitpid鸭丛。慢速系統(tǒng)調用會被信號處理函數(shù)打斷,可能會返回EINTR錯誤唐责,也可能會自動重啟鳞溉。我們編寫捕獲信號的程序時,必須對此有所準備鼠哥。例如熟菲,對accept的處理,connect被打斷后就不能被使用了朴恳。
wait和waitpid函數(shù)
通過wait和waitpid都可以獲得終止的子進程的pid和狀態(tài)抄罕,waitpid還能指定想等待的pid,options參數(shù)允許指定附加選項于颖,最常用的是WNOHANG呆贿,在沒有終止子進程時不阻塞。
信號阻塞期間如果該信號產(chǎn)生了多次森渐,解除阻塞后只能接收到一次做入,因此要用waitpid(-1,*,WNOHANG)來循環(huán)回收所有結束的子進程。
accept返回前連接中止
三路握手完成同衣,連接建立后竟块,客戶TCP發(fā)送了RST,服務器端在調用accept前收到了這個RST耐齐。
如何處理這種中止依賴于不同的實現(xiàn)浪秘。BSD的實現(xiàn)是在內(nèi)核中處理蒋情,服務器的accept繼續(xù)阻塞,SVR4的實現(xiàn)是返回一個錯誤給進程秫逝。如果返回了一個錯誤恕出,再次調用accept就行。
服務器進程終止
客戶與服務器連接成功后违帆,服務器進程如果終止(被動)浙巫,套接字被關閉,向客戶發(fā)送FIN刷后,客戶響應ACK的畴,此時客戶進程可能阻塞在用戶輸出上,看不到這個RST尝胆,此時如果進行write丧裁,再read,就會收到預期外的EOF含衔。
SIGPIPE信號
寫一個已收到FIN的套接字會收到RST煎娇,寫一個已收到RST的套接字會產(chǎn)生SIGPIPE信號。
如果沒有特殊的事情要做贪染,就將SIGPIPE設置為SIG_IGN缓呛,忽略它,并在后面的讀寫操作中檢查返回的錯誤杭隙。如果需要采取特殊措施(如寫入日志)哟绊,就要捕捉該信號。但如果用了多個套接字痰憎,信號處理程序無法分辨是哪個套接字出的錯票髓。如果需要知道出錯的位置,要么不理會該信號铣耘,要么從信號處理函數(shù)返回后再處理write的EPIPE洽沟。
服務器主機崩潰 服務器主機關機
服務器主機崩潰時,已有的網(wǎng)絡連接上不發(fā)出任何東西(如FIN)涡拘×崆客戶對服務器的寫操作會持續(xù)重傳數(shù)據(jù),試圖接收一個ACK鳄乏,直到超時跷车。客戶隨后的readline調用會返回一個錯誤橱野。
可以對readline設置一個超時朽缴。如果不主動向服務器發(fā)送數(shù)據(jù)也想檢測出服務器主機的崩潰,需要SO_KEEPALIVE套接字選項水援。
服務器主機崩潰后重啟
服務器主機在崩潰后客戶發(fā)數(shù)據(jù)前重啟完成密强,客戶不知道服務器主機的崩潰茅郎,發(fā)送數(shù)據(jù),但服務器TCP丟失了之前的連接信息或渤,因此響應RST系冗,客戶TCP收到RST時,客戶進程正阻塞于readline調用薪鹦,該調用返回一個錯誤掌敬。
數(shù)據(jù)格式
例子:在客戶與服務器之間傳遞文本串
用sscanf獲取文本中的指定數(shù)據(jù),再用snprintf把結果轉換為文本串池磁。
例子:在客戶與服務器之間傳遞二進制結構
當這樣的客戶和服務器程序運行在字節(jié)序不一樣的或某些類型長度不一致的兩個主機上時奔害,工作將失常。
不同的實現(xiàn)在存儲二進制數(shù)據(jù)的格式上(大端小端)地熄、相同類型的長度上华临、給結構打包的方式(對齊)上都可能不同,因此直接傳送二進制結構絕不明智端考。
解決方法:
所有的數(shù)值數(shù)據(jù)作為文本串來傳遞雅潭。
顯式定義所支持數(shù)據(jù)類型的進進制格式,并以這樣的格式在客戶與服務器間傳遞所有數(shù)據(jù)却特。