我寫了一個服務器程序, 在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。
1
2
|
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
Program received signal SIGPIPE, Broken pipe
http://blog.csdn.net/xiaobai1593/article/details/7469233
我寫了一個服務器程序, 在Windows下在cygwin環境編譯后執行, 然后用C#寫了多線程客戶端進行壓力測試. 程序一直運行正常. 但當在Linux下測試時, 總是莫名退出. 最后跟蹤到是write調用導致退出. 用gdb執行程序, 退出時提示"Broken pipe".
最后問題確定為, 對一個對端已經關閉的socket調用兩次write, 第二次將會生成SIGPIPE信號, 該信號默認結束進程.
具體的分析可以結合TCP的"四次握手"關閉. TCP是全雙工的信道, 可以看作兩條單工信道, TCP連接兩端的兩個端點各負責一條. 當對端調用close時, 雖然本意是關閉整個兩條信道, 但本端只是收到FIN包. 按照TCP協議的語義, 表示對端只是關閉了其所負責的那一條單工信道, 仍然可以繼續接收數據. 也就是說, 因為TCP協議的限制, 一個端點無法獲知對端的socket是調用了close還是shutdown.
截圖來自: UNPv1
對一個已經收到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信號的方法
http://hi.baidu.com/greathongjian/blog/item/2f695643091885139213c65a.html
struct sigaction sa;
sa.sa_handler = SIG_IGN;//設定接受到指定信號后的動作為忽略
sa.sa_flags = 0;
if (sigemptyset(&sa.sa_mask) == -1 || //初始化信號集為空
sigaction(SIGPIPE, &sa, 0) == -1) { //屏蔽SIGPIPE信號
perror("failed to ignore SIGPIPE; sigaction");
exit(EXIT_FAILURE);
}
pthread線程里如何屏蔽SIGPIPE異常
hi.baidu.com/ailacy/blog/item/a7eb65f8b8b55707d8f9fdd5.html
http://bbs2.chinaunix.net/viewthread.php?tid=985166&extra=&page=1
在pthread中,可能會遇到Program received signal SIGPIPE, Broken pipe的問題,解決方法是每一個線程啟動之前時,先執行下面代碼:
#ifndef WIN32
sigset_t signal_mask;
sigemptyset (&signal_mask);
sigaddset (&signal_mask, SIGPIPE);
int rc = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
if (rc != 0) {
printf("block sigpipe error/n");
}
#endif
當然,這只是多種方法之一~
根據賴半仙的使用經驗,只要在main函數一開始就寫入上面這段代碼,就能屏蔽掉pthread線程中的SIGPIPE
[linux] SIGPIPE信號及其處理
http://hi.baidu.com/mckeyzhang/blog/item/d647f26034eee542eaf8f823.html
在linux下寫socket的程序的時候,如果嘗試send到一個disconnected socket上,就會讓底層拋出一個SIGPIPE信號。
這個信號的缺省處理方法是退出進程,大多數時候這都不是我們期望的。因此我們需要重載這個信號的處理方法。調用以下代碼,即可安全的屏蔽SIGPIPE:
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );
//======================================================================
SIGPIPE
From Wikipedia, the free encyclopedia
Jump to: navigation, search
SIGPIPE
Description Write on a pipe with no one to read it
Default action Abnormal termination of the process
SA_SIGINFOmacros
one
On POSIX-compliant platforms, SIGPIPE is the signal raised when a computer program attempts to write to apipe without a process connected to the other end. The symbolic constant for SIGPIPE is defined in the header file signal.h. Symbolic signal names are used because signal numbers can vary across platforms.
Etymology
SIG is a common prefix for signal names. PIPE refers to the Unix pipe.
Description
Unix supports the principle of piping, which allows processes to send data to other processes without the need for creating temporary files. When a pipe is broken, the process writing to it is sent the SIGPIPE signal. The default reaction to this signal for a process is to terminate.
A simple example of piping is the following.
ps l | head
This command, when run on a Unix-like machine (including Linux), returns a list of processes, limited to ten lines.
ps l returns a list of all processes (including those of other users).
head selects the first ten lines.
When ps has written ten lines, head has received all it needs and exits. ps will receive a SIGPIPE when it tries to write the remaining lines, causing it to terminate as well: It is no use writing data that no one will use. It is also possible that the reading process terminates while reading the data. This will also cause SIGPIPE to be sent to the writing process.
One can ignore SIGPIPE (using, for example, the signal system call). In this case, all system calls that would cause SIGPIPE to be sent will return -1 and set errno to EPIPE.
Uinx 下 Broken pipe 問題
www.javaeye.com/topic/456975#
前段時間在處理延時函數時遇到過 "Alarm clock" 信號問題(見我的 "Unix C 延時函數小結")。現在測試中還遇到了 "Broken pipe" 信號問題,同樣產生這個信號程序就中止了。
我的程序產生這個信號的原因是:
client端通過 pipe 發送信息到server端后,就關閉client端, 這時server端,返回信息給 client 端時就產生Broken pipe 信號了。
對於產生信號,我們可以在產生信號前利用方法 signal(int signum, sighandler_t handler) 設置信號的處理。如果沒有調用此方法,系統就會調用默認處理方法:中止程序,顯示提示信息(就是我們經常遇到的問題)。我們可以調用系統的處理方法,也可 以自定義處理方法。
系統里邊定義了三種處理方法:
1)SIG_DFL
2)SIG_IGN
3)SIG_ERR
項目中我調用了 signal(SIGALRM, SIG_IGN) 和 signal(SIGPIPE, SIG_IGN), 這樣產生 SIGALAM 和 SIGPIPE 信號時就不會中止程序,直接把這個信號忽略掉。
自定義處理方法:
C代碼
void signal_handle(ing signo)
{
//do something;
}
int main()
{
signal(SIGPIPE, signal_handle);
......
}
聲明:JavaEye文章版權屬於作者,受法律保護。沒有作者書面許可不得轉載。