關於網路編程時候SIGPIPE訊號__編程

來源:互聯網
上載者:User

在linux下寫socket的程式的時候,如果嘗試 send 到一個 disconnected 的 socket上,(讀取端已經關閉)就會讓底層拋出一個SIGPIPE訊號。該訊號預設情況下會終止當前進程,大多數時候這都不是我們期望的。


首先用代碼測試正常的 server client 端TCP通訊:
http://blog.csdn.net/xsckernel/article/details/8543352

 

然後在伺服器端  accept 成功之後調用close(fd)模仿這種情形。

  [cpp]  view plain copy   while(1)   {           if((connect_fd = accept(listen_fd,(struct sockaddr *)&peer_addr,&addr_len)) < 0)                   continue;                 pid = fork();           if(pid == 0){                   close(listen_fd);                   close(connect_fd);                   printf("peer ip : %s\n",inet_ntoa(peer_addr.sin_addr));                   printf("peer port : %d\n",ntohs(peer_addr.sin_port));                         /*read(connect_fd,buf,sizeof(buf));                  write(connect_fd,buf,strlen(buf) + 1);*/           }else{                    close(connect_fd);           }   }  


再次測試 如果再在用戶端上連續輸入兩次,用戶端就會退出。

 

 

為了驗證用戶端是接收到 SIGPIPE 訊號而退出的,因此在用戶端的代碼裡添加下面一個函數:

  [cpp]  view plain copy   void sig_pipe(int sign)   {           printf("Catch a SIGPIPE signal\n");   }  


然後在主函數裡添加  signal(SIGPIPE, sig_pipe); 函數,
並加上標頭檔:
#include <signal.h>

 

 

接著做上面的實驗,那麼會在用戶端一邊看到輸出:

  [plain]  view plain copy   shichao@ubuntu:~/Downloads$ ./client 127.0.0.1 8888   >aaa   recv :    >bbb   Catch a SIGPIPE signal   recv :    >Love you   Catch a SIGPIPE signal   recv :    >Girl   Catch a SIGPIPE signal   recv :   

 

 

現在來考察一下,為什麼上面的輸出中需要連續兩次才會導致用戶端退出。可以用 wireshark 這樣的嗅探器進行相關資料分析。

 

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

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

 


因此我們需要重載這個訊號的處理方法。調用以下代碼,即可安全的屏蔽SIGPIPE:

  [cpp]  view plain copy   struct sigaction sa;   sa.sa_handler = SIG_IGN;   sigaction( SIGPIPE, &sa, 0 );  



 

注意對於上面測試用到的用戶端代碼因為兩次發送都是使用者輸入的,時間間隔比較長。 但如果是由程式自動發送那麼有時候捕捉不到SIGPIPE訊號。這是因為第二次調用send函數發送資料時有可能伺服器端的RST還未返回。   在第二次發送資料前sleep一下程式,使得RST能在send前返回。再次測試程式,這樣就都能捕捉到SIPPIPE訊號了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.