參考資料:
1.博客1:https://blog.csdn.net/qq_37535749/article/details/113781338
2.博客2:https://blog.csdn.net/zkkdcs1/article/details/88659069
3.博客3:https://superuser.com/questions/554855/how-can-i-fix-a-broken-pipe-error
4.博客4:https://www.cnblogs.com/cthon/p/9139553.html
#include <stdlib.h> #include <sys/signal.h> //默認讀寫一個關閉的socket會觸發sigpipe信號 該信號的默認操作是關閉進程 有時候這明顯是我們不想要的 //所以此時我們需要重新設置sigpipe的信號回調操作函數 比如忽略操作等 使得我們可以防止調用它的默認操作 //信號的處理是異步操作 也就是說 在這一條語句以后繼續往下執行中如果碰到信號依舊會調用信號的回調處理函數 //處理sigpipe信號 void handle_for_sigpipe() { struct sigaction sa; //信號處理結構體 memset(&sa, '\0', sizeof(sa)); sa.sa_handler = SIG_IGN;//設置信號的處理回調函數 這個SIG_IGN宏代表的操作就是忽略該信號 sa.sa_flags = 0; if(sigaction(SIGPIPE, &sa, NULL))//將信號和信號的處理結構體綁定 return; } int main(int argc, char *argv[]) { handle_for_sigipipe(); while(1){} return 0; }
今天在原程序中需要新加若干個http接口,用來請求server端的版本信息,代碼寫完后放到開發環境測試,
一直在報broken pipe的錯誤,
后來經過排查,定位到時http client請求server數據的地方有錯誤。
回憶pipe的特性,pipe如果是兩個父子進程掌控讀端和寫端,可能出現,“子進程寫端關閉,父進程讀端未關閉,產生僵屍進程”的現象。
用這個思路查閱了相關資料https://blog.csdn.net/zqz_zqz/article/details/52235479
摘除博客中的一句話:客戶端讀取超時關閉了連接,這時候服務器端再向客戶端已經斷開的連接寫數據時就發生了broken pipe異常!
今天回溯了一下這個錯誤,發現了一篇講的比較透徹的博客:https://www.cnblogs.com/cthon/p/9139553.html
問題:
寫了一個server和一個client,UNIX套接字的,server不斷接收消息並打印出來,client是一個交互程序,輸入一個消息回車發送,接着又可以輸入消息。
出問題了:
當server監聽着,client第一次發送消息成功,server接收並打印出來了。
client第二次發送消息沒成功並且結束程序了,server沒接收到消息,保持繼續監聽。
我用GDB調試時,發現client第二次發送消息時,client收到SIGPIPE(Broken Pipe)信號。server明明還監聽着,而且再次啟動client還是第一次成功,第二次失敗退出。
同樣的,當client因為斷開(關閉了網絡描述符sfd,或者ctrl+c/ctrl+\異常斷開),server端也產生SIGPIPE信號。
分析:
TCP協議是端到端的傳輸控制協議,之所以是“端到端”的協議,是因為”路由“是由IP協議負責的,TCP協議負責為兩個通信端點提供可靠性保證,這個可靠性不是指一個端點發送的數據,另一個端點肯定能收到(這顯然是不可能的),而是指,數據的可靠投遞或者故障的可靠通知。
所謂的“端到端”,指的是在通信兩端之間建立了一個全雙工的通信管道,既然是管道,就不得不了解管道。
管道的特點:
- 管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;
- 只能用於父子進程或者兄弟進程之間(具有親緣關系的進程);
- 單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,並且只存在與內存中。
- 數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,並且每次都是從緩沖區的頭部讀出數據。
管道特性的表現:
- 如果一個進程以只寫打開管道,但是沒有以只讀或讀寫打開這個管道的進程,則打開操作會阻塞, 直到有進程以讀或讀寫打開,open 才會返回。(寫端打開,讀端關閉)
- 如果一個進程以只讀打開管道,但是沒有以只寫或讀寫打開這個管道的進程,則打開操作會阻塞, 直到有進程以寫或讀寫打開,open 才會返回。(寫端關閉,讀端打開)
- 當寫端沒有寫入數據時,讀端會阻塞到 read 調用,直到寫端寫入數據或者寫端關閉。 當管道沒有空間時,再寫入數據就會被阻塞。直到有進程讀取數據,或者所有的讀端關閉。(讀寫順序)
注意:全雙工,指的是每一端都可讀可寫。前提是對端打開。如果對端都關閉了,本端讀數據為空,不會出錯;但本段寫數據肯定出錯。
總結:
如果要進行順利的管道通信:管道的兩端必需都打開。
- 管道讀端關閉,寫端不能寫,否則會發出SIGPIPE信號,即會生成BROKEN PIPE錯誤。
也就是說tcp通信時,client端通過 pipe發送信息到server端后,client端掛不必,這時server端返回信息,向pipe些內容,就會出錯。
解決方法:
1、signal(SIGPIPE,SIG_INT);//(全局范圍內)
2、setsocketop;//(tcp特性設置)
/// sock 就是設置不發送 `SIGPIPE` 信號的 socket 變量 int value = 1; setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
- 合理規避讀端關閉,寫端打開的問題。(避免client端關閉,server端發送數據這種情況)