今天在調試客戶端時,發現忽然程序退出,沒有什麼提示,gdb,發如今TCP發送數據時,收到了SIGPIPEhtml
當服務器close一個鏈接時,若client端接着發數據。根據TCP協議的規定,會收到一個RST響應,client再往這個服務器發送數據時,系統會發出一個SIGPIPE信號給進程,告訴進程這個鏈接已經斷開了,不要再寫了。
根據信號的默認處理規則SIGPIPE信號的默認執行動做是terminate(終止、退出),因此client會退出。若不想客戶端退出能夠把SIGPIPE設爲SIG_IGN
如: signal(SIGPIPE,SIG_IGN);
這時SIGPIPE交給了系統處理。
服務器採用了fork的話,要收集垃圾進程,防止殭屍進程的產生,能夠這樣處理:
signal(SIGCHLD,SIG_IGN); 交給系統init去回收。
這裏子進程就不會產生殭屍進程了。
http://www.cublog.cn/u/31357/showart_242605.html
很久沒作過C開發了,最近重操舊業。服務器
|
爲了方便觀察錯誤輸出,lib/writen.c也作了修改,加了些日誌:app
|
client.c放在tcpclieserv目錄下,修改了Makefile,增長了client.c的編譯目標tcp
|
接着就能夠開始測試了。測試
本機服務端:ui
修改客戶端代碼,catch sigpipe信號this
|
本機服務端:
爲了方便操做,加大1次write的數據量,修改MAXBUF爲4096000
本機服務端:
爲何以上測試,都是對方已經中斷socket後,發送端再次write,結果會有所不一樣呢。從後來找到的UNP5.12,5.13能找到答案
The client's call to readline may happen before the server's RST is received by the client, or it may happen after. If the readline happens before the RST is received, as we've shown in our example, the result is an unexpected EOF in the client. But if the RST arrives first, the result is an ECONNRESET ("Connection reset by peer") error return from readline. |
以上解釋了測試3的現象,write時,收到RST.
What happens if the client ignores the error return from readline and writes more data to the server? This can happen, for example, if the client needs to perform two writes to the server before reading anything back, with the first write eliciting the RST. The rule that applies is: When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated. If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE. |
以上解釋了測試1,2的現象,write一個已經接受到RST的socket,系統內核會發送SIGPIPE給發送進程,若是進程catch/ignore這個信號,write都返回EPIPE錯誤.
所以,UNP建議應用根據須要處理SIGPIPE信號,至少不要用系統缺省的處理方式處理這個信號,系統缺省的處理方式是退出進程,這樣你的應用就很難查到處理進程爲何退出。
在Unix系統下,若是send在等待協議傳送數據時網絡斷開的話,調用send的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。
在Unix系統下,若是recv函數在等待協議接收數據時網絡斷開了,那麼調用recv的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。
處理方法:
在初始化時調用signal(SIGPIPE,SIG_IGN)忽略該信號(只需一次)
其時send或recv函數將返回-1,errno爲EPIPE,可視狀況關閉socket或其餘處理
gdb:
gdb默認收到sigpipe時中斷程序,可調用handle SIGPIPE nostop print
相關
(1)SIG_DFL信號專用的默認動做:
(a)若是默認動做是暫停線程,則該線程的執行被暫時掛起。當線程暫停期間,發送給線程的任何附加信號都不交付,直到該線程開始執行,可是SIGKILL除外。
(b)把掛起信號的信號動做設置成SIG_DFL,且其默認動做是忽略信號 (SIGCHLD)。
(2)SIG_IGN忽略信號
(a)該信號的交付對線程沒有影響
(b)系統不容許把SIGKILL或SIGTOP信號的動做設置爲SIG_DFL
(3)指向函數的指針--捕獲信號
(a)信號一經交付,接收線程就在指定地址上執行信號捕獲程序。在信號捕 獲函數返回後,接受線程必須在被中斷點恢復執行。
(b)用C語言函數調用的方法進入信號捕捉程序:
void func (signo)
int signo;
func( )是指定的信號捕捉函數,signo是正被交付信號的編碼
(c)若是SIGFPE,SIGILL或SIGSEGV信號不是由C標準定義的kill( )或raise( )函數所生成,則從信號SIGFPE,SIGILL,SIGSEGV的信號捕獲函數正常返回後線程的行爲是未定義的。
(d)系統不容許線程捕獲SIGKILL和SIGSTOP信號。
(e)若是線程爲SIGCHLD信號創建信號捕獲函數,而該線程有未被等待的以終止的子線程時,沒有規定是否要生成SIGCHLD信號來指明那個子線程。
每一種信號都被OSKit給予了一個符號名,對於32位的i386平臺而言,一個字32位,於是信號有32種。下面的表給出了經常使用的符號名、描述和它們的信號值。
符號名 信號值 描述 是否符合POSIX
SIGHUP 1 在控制終端上檢測到掛斷或控制線程死亡 是
SIGINT 2 交互注意信號 是
SIGQUIT 3 交互停止信號 是
SIGILL 4 檢測到非法硬件的指令 是
SIGTRAP 5 從陷阱中回朔 否
SIGABRT 6 異常終止信號 是
SIGEMT 7 EMT 指令 否
SIGFPE 8 不正確的算術操做信號 是
SIGKILL 9 終止信號 是
SIGBUS 10 總線錯誤 否
SIGSEGV 11 檢測到非法的內存調用 是
SIGSYS 12 系統call的錯誤參數 否
SIGPIPE 13 在無讀者的管道上寫 是
SIGALRM 14 報時信號 是
SIGTERM 15 終止信號 是
SIGURG 16 IO信道緊急信號 否
SIGSTOP 17 暫停信號 是
SIGTSTP 18 交互暫停信號 是
SIGCONT 19 若是暫停則繼續 是
SIGCHLD 20 子線程終止或暫停 是
SIGTTIN 21 後臺線程組一成員試圖從控制終端上讀出 是
SIGTTOU 22 後臺線程組的成員試圖寫到控制終端上 是
SIGIO 23 容許I/O信號 否
SIGXCPU 24 超出CPU時限 否
SIGXFSZ 25 超出文件大小限制 否
SIGVTALRM 26 虛時間警報器 否
SIGPROF 27 側面時間警報器 否
SIGWINCH 28 窗口大小的更改 否
SIGINFO 29 消息請求 否
SIGUSR1 30 保留做爲用戶自定義的信號1 是
SIGUSR2 31 保留做爲用戶自定義的信號 是
注意:Linux 信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始,後來在實踐中暴露出一些問題,所以,把那些創建在早期機制上 的信號叫作"不可靠信號",信號值小於SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進程每次處理信號後, 就將對信號的響應設置爲默認動做。在某些狀況下,將致使對信號的錯誤處理;所以,用戶若是不但願這樣的操做,那麼就要在信號處理函數結尾再一次調用 signal(),從新安裝該信號。
另外,我再作一些補充,產生RST響應以致於系統發出SIGPIPE信號,應該分爲兩種狀況:
1. 客戶端到服務端之間網絡斷掉,或者服務端斷電等,物理鏈接斷掉了,這種狀況下客戶端不會退出,send函數正常執行,不會感受到本身出錯。由於因爲物理網 絡斷開,服務端不會給客戶端迴應錯誤消息,沒有RST響應,天然也不會產生SIGPIPE信號。可是當服務端再恢復正常的時候,對客戶端send來的消息 會產生RST響應,客戶端就收到SIGPIPE信號了,程序退出,可是這時send函數是可以返回 -1的。能夠進行異常處理。
2.客戶端到服務端的網絡能通,服務程序掛掉,客戶端程序會立刻退出,由於服務端能正常返回錯誤消息,客戶端收到,SIGPIPE信號就產生了。不過我不肯定此時服務端返回是的RST響應,抓包來看沒有RST標誌。水平有限,只寫到這了。