socket編程—— 伺服器遇到Broken Pipe崩潰

來源:互聯網
上載者:User

我寫了一個伺服器程式, 在Linux下測試時, 總是莫名退出. 最後跟蹤到是write調用導致退出. 用gdb執行程式, 退出時提示"Broken pipe".

最後問題確定為, 對一個對端已經關閉的socket調用兩次write, 第二次將會產生SIGPIPE訊號, 該訊號預設結束進程.

具體的分析可以結合TCP的"四次握手"關閉. TCP是全雙工系統的通道, 可以看作兩條單工通道, TCP串連兩端的兩個端點各負責一條. 當對端調用close時, 雖然本意是關閉整個兩條通道, 但本端只是收到FIN包. 按照TCP協議的語義, 表示對端只是關閉了其所負責的那一條單工通道, 仍然可以繼續接收資料. 也就是說, 因為TCP協議的限制, 一個端點無法獲知對端的socket是調用了close還是shutdown.

對一個已經收到FIN包的socket調用read方法, 如果接收緩衝已空, 則返回0, 這就是常說的表示串連關閉. 但第一次對其調用write方法時, 如果發送緩衝沒問題, 會返回正確寫入(發送). 但發送的報文會導致對端發送RST報文, 因為對端的socket已經調用了close, 完全關閉, 既不發送, 也不接收資料. 所以, 第二次調用write方法(假設在收到RST之後), 會產生SIGPIPE訊號, 導致進程退出.

為了避免進程退出, 可以捕獲SIGPIPE訊號, 或者忽略它, 給它設定SIG_IGN訊號處理函數:

signal(SIGPIPE, SIG_IGN);

這樣, 第二次調用write方法時, 會返回-1, 同時errno置為SIGPIPE. 程式便能知道對端已經關閉.

PS: Linux下的SIGALRM似乎會每1秒鐘往後位移1毫秒, 但Windows下經過測試完全準時, 不差1毫秒.

當然還有其他方法來處理SIGPIPE

     設定當前socket在進行寫操作時不產生SIGPIPE。

?
12 int

set = 1;
setsockopt(sd,
SOL_SOCKET, SO_NOSIGPIPE, (
void

*)&set,
sizeof(int));

這樣做的好處在於:在某些情況 下我們並不需要一個全域的SIGPIPE handler。但是據說這種並不通用,在linux下沒有相應的定義—-但我在mac下測試通過。

轉自 http://blog.csdn.net/mustanglau/article/details/4485491

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.