今天寫程式遇到這個問題,搜尋一番之後覺得該文較好,於是轉了過來。
當伺服器 close 乙個連線時,若 client 繼續向伺服器發資料,根據 tcp 協議的規定,客戶端會收到乙個 rst 響應,client再往這個伺服器傳送資料時,系統會發出乙個 sigpipe 訊號給客戶端程序,導致客戶端程序退出。
具體分析可以結合 tcp 的「四次握手」關閉。tcp 是全雙工的通道,可以看作兩條單雙工的通道,tcp 連線兩端的兩個端點各負責一條。當對端呼叫 close 時,雖然預設行為是關閉整個兩個通道,但本端只是收到 fin 包,按照 tcp 協議的語義,表示對端只是關閉了其所負責的那一條單工通道,本端仍然可以接收資料。也就是說,因為 tcp 協議的限制,乙個端點無法獲知對端的 socket 是呼叫了 close 還是 shutdown。
假設server和client 已經建立了連線,server呼叫了close, 傳送fin 段給client(其實不一定會傳送fin段,close不能保證,只有當某個sockfd的引用計數為0,close 才會傳送fin段,否則只是將引用計數減1而已。也就是說只有當所有程序(可能fork多個子程序都開啟了這個套接字)都關閉了這個套接字,close 才會傳送fin 段),此時server不能再通過socket傳送和接收資料,此時client呼叫read,如果接收到fin 段會返回0。但client此時還是可以write 給server的,write呼叫只負責把資料交給tcp傳送緩衝區就可以成功返回了,所以不會出錯,
而server收到資料後應答乙個rst段,表示伺服器已經不能接收資料,連線重置,client收到rst段後無法立刻通知應用層,只把這個狀態儲存在tcp協議層。如果client再次呼叫write發資料給server,由於tcp協議層已經處於rst狀態了,因此不會將資料發出,而是發乙個sigpipe訊號給應用層,sigpipe訊號的預設處理動作是終止程式。
有時候**中需要連續多次呼叫write,可能還來不及呼叫read得知對方已關閉了連線就被sigpipe訊號終止掉了,這就需要在初始化時呼叫sigaction處理sigpipe訊號,對於這個訊號的處理我們通常忽略即可,signal(sigpipe, sig_ign); 如果sigpipe訊號沒有導致程序異常退出(捕捉訊號/忽略訊號),write返回-1並且errno為epipe(broken pipe)。(非阻塞地write)
close 關閉了自身資料傳輸的兩個方向。
#include int shutdown(int sockfd, int how);
shutdown 可以選擇關閉某個方向或者同時關閉兩個方向,shutdown how = 0 or how = 1 or how = 2 (shut_rdorshut_wrorshut_rdwr),後兩者可以保證對等方接收到乙個eof字元(即傳送了乙個fin段),而不管其他程序是否已經開啟了這個套接字。所以說,如果是呼叫shutdown how = 1 ,則意味著往乙個已經傳送出fin的套接字中寫是允許的,接收到fin段僅代表對方不再傳送資料,但對方還是可以讀取資料的,可以讓對方可以繼續讀取緩衝區剩餘的資料。
對於產生訊號,系統裡邊定義了三種處理方法:
(1)sig_dfl訊號專用的預設動作:
(a)如果預設動作是暫停執行緒,則該執行緒的執行被暫時掛起。當執行緒暫停期間,傳送給執行緒的任何附加訊號都不交付,直到該執行緒開始執行,但是sigkill除外。
(b)把掛起訊號的訊號動作設定成sig_dfl,且其預設動作是忽略訊號 (sigchld)。
(2)sig_ign忽略訊號
(a)該訊號的交付對執行緒沒有影響
(b)系統不允許把sigkill或sigtop訊號的動作設定為sig_dfl
3)sig_err
sigign:忽略改訊號
sigdef:採用系統預設方式處理訊號
sigchld:在乙個程序終止或者停止時,將該訊號傳送給其父程序,父程序的wait函式通常用來捕捉這個訊號
sigint:當使用者按中斷鍵(delete/ctrl+c)時將產生這個訊號
sigkill:此訊號可以用於殺死乙個程序
sigstop:這是個作業控制訊號,用於停止乙個程序 這個訊號和sigkill是僅有的兩個不能**獲或忽略的訊號
sysusr1/2:使用者定義的訊號,用於應用程式
SIGPIPE訊號處理
在unix下寫socket程式,會遇到連線建立,若某一端關閉連線,而另一端仍然向它寫資料,第一次寫資料後會收到rst響應,此後再寫資料,核心將向程序發出sigpipe訊號,通知程序此連線已經斷開。而sigpipe訊號的預設處理是終止程式,導致上述問題的發生。為避免這種情況,可以選擇忽略sigpipe...
gdb中忽略訊號處理 SIGPIPE
gdb除錯網路程式時,會遇到sigpipe資訊,預設gdb會把程式停下來,即使程式使用signal sigpipe,sig ign 來忽略訊號。用handle命令設定一下預設的signal的處理行為即可 在gdb模式下執行下面的命令 handle sigpipe nostop print 如果連資訊...
關於SIGPIPE訊號
我寫了乙個伺服器程式,在linux下測試,然後用c 寫了客戶端用千萬級別數量的短鏈結進行壓力測試.但是伺服器總是莫名退出,沒有core檔案.最後問題確定為,對乙個對端已經關閉的socket呼叫兩次write,第二次將會生成sigpipe訊號,該訊號預設結束程序.具體的分析可以結合tcp的 四次握手 ...