在 TCP 通訊雙方中,為了描述方便,以下將通訊雙方以 A 和 B 取代。
根據TCP協定規定,如果A關閉連線後B繼續發送數據,B會收到A的RST回應。若B繼續發送數據,系統會發出SIGPIPE訊號告知連接已斷開,停止發送。
系統對 SIGPIPE 訊號的預設處理行為是讓 B 進程退出。
作業系統對 SIGPIPE 訊號的這種預設處理行為非常不友好,讓我們來分析一下。
#TCP通訊是全雙工頻道,相當於兩條單工頻道,連接兩端各負責一條。
當對端「關閉」時, 雖然本意是關閉整個兩個頻道,但本端只是收到 FIN 包。
根據TCP協定的規定,當一端關閉其負責的單向通道時,仍可接收資料但不再發送資料。
也就是說,因為 TCP 協定的限制,通訊一方無法獲知對端的 socket 是呼叫了 close 還是 shutdown。
int shutdown(int socket, int how);
shutdown 函數的參數 how 可設定為關閉 SHUT_RD、SHUT_WR 或 SHUT_RDWR 用於表示關閉收、發送單一通道或同時關閉收發通道。
對一個已經收到 FIN 套件的 socket 呼叫 read/recv 方法, 如果接收緩衝已空,則傳回 0,這就是常說的表示連線關閉。但第一次對其呼叫 write/send 方法時,如果發送緩衝沒問題,會傳回正確寫入(即 write/send 函數傳回值大於 0),但發送的封包會導致對端回應 RST 封包。因為上一次程式呼叫 write/send 是正常的,再次嘗試呼叫 write/send 函數時因產生 SIGPIPE 訊號導致進程退出。
這種預設行為對於我們開發程序,尤其是對於後端服務,需要同時對許多客戶端服務,不能因為與某一個客戶端的連接出問題了而導致整個進程退出不能繼續為其他客戶端服務。
為了避免這種現像出現, 可以捕獲 SIGPIPE 訊號並對其進行處理或忽略該訊號, 忽略該訊號代碼如下:
signal(SIGPIPE, SIG_IGN);
這樣設定後,第二次呼叫 write/send 方法時,會回傳 -1,同時 errno 錯誤碼被置為 SIGPIPE,程式便能知道對端已經關閉。
以上是Linux SIGPIPE訊號的詳細內容。更多資訊請關注PHP中文網其他相關文章!