當服務器close一個鏈接時,若client端接着發數據。根據TCP協議的規定,會收到一個RST響應,client再往這個服務器發送數據時,系統會發出一個SIGPIPE信號給進程,告訴進程這個鏈接已經斷開了,不要再寫了。 linux
又或者當一個進程向某個已經收到RST的socket執行寫操做是,內核向該進程發送一個SIGPIPE信號。該信號的缺省學位是終止進程,所以進程必須捕獲它以避免不情願的被終止。
安全
根據信號的默認處理規則SIGPIPE信號的默認執行動做是terminate(終止、退出),因此client會退出。若不想客戶端退出能夠把 SIGPIPE設爲SIG_IGN 服務器
如:signal(SIGPIPE, SIG_IGN);
這時SIGPIPE交給了系統處理。 socket
服務器採用了fork的話,要收集垃圾進程,防止殭屍進程的產生,能夠這樣處理:
signal(SIGCHLD,SIG_IGN);
交給系統init去回收。
這裏子進程就不會產生殭屍進程了。 函數
在linux下寫socket的程序的時候,若是嘗試send到一個disconnected socket上,就會讓底層拋出一個SIGPIPE信號。
這個信號的缺省處理方法是退出進程,大多數時候這都不是咱們指望的。所以咱們須要重載這個信號的處理方法。調用如下代碼,便可安全的屏蔽SIGPIPE:
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 ); this
signal設置的信號句柄只能起一次做用,信號被捕獲一次後,信號句柄就會被還原成默認值了。
sigaction設置的信號句柄,能夠一直有效,值到你再次改變它的設置。 spa
struct sigaction action;
action.sa_handler = handle_pipe;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGPIPE, &action, NULL);
void handle_pipe(int sig)
{
//不作任何處理便可
} 接口
RST的含義爲「復位」,它是TCP在某些錯誤狀況下所發出的一種TCP分節。有三個條件能夠產生RST:
1), SYN到達某端口但此端口上沒有正在監聽的服務器。
2), TCP想取消一個已有鏈接
3), TCP接收了一個根本不存在的鏈接上的分節。
1. Connect 函數返回錯誤ECONNREFUSED:
若是對客戶的SYN的響應是RST,則代表該服務器主機在咱們指定的端口上沒有進程在等待與之鏈接(例如服務器進程也許沒有啓動),這稱爲硬錯(hard error),客戶一接收到RST,立刻就返回錯誤ECONNREFUSED.
TCP爲監聽套接口維護兩個隊列。兩個隊列之和不超過listen函數第二個參數backlog。
當一個客戶SYN到達時,若兩個隊列都是滿的,TCP就忽略此分節,且不發送RST.這個由於:這種狀況是暫時的,客戶TCP將重發SYN,指望不久就能 在隊列中找到空閒條目。要是TCP服務器發送了一個RST,客戶connect函數將當即發送一個錯誤,強制應用進程處理這種狀況,而不是讓TCP正常的 重傳機制來處理。還有,客戶區別不了這兩種狀況:做爲SYN的響應,意爲「此端口上沒有服務器」的RST和意爲「有服務器在此端口上但其隊列滿」的 RST.
Posix.1g容許如下兩種處理方法:忽略新的SYN,或爲此SYN響應一個RST.歷史上,全部源自Berkeley的實現都是忽略新的SYN。
2.若是殺掉服務器端處理客戶端的子進程,進程退出後,關閉它打開的全部文件描述符,此時,當服務器TCP接收到來自此客戶端的數據時,因爲先前打開的那個套接字接口的進程已終止,因此以RST響應。
常常遇到的問題:
若是不判斷read , write函數的返回值,就不知道服務器是否響應了RST, 此時客戶端若是向接收了RST的套接口進行寫操做時,內核給該進程發一個SIGPIPE信號。此信號的缺省行爲就是終止進程,因此,進程必須捕獲它以避免不情願地被終止。
進程不管是捕獲了該信號並從其信號處理程序返回,仍是不理會該信號,寫操做都返回EPIPE錯誤。 隊列
3. 服務器主機崩潰後重啓
若是服務器主機與客戶端創建鏈接後崩潰,若是此時,客戶端向服務器發送數據,而服務器已經崩潰不能響應客戶端ACK,客戶TCP將持續重傳數據分節,試圖從服務器上接收一個ACK,若是服務器一直崩潰客戶端會發現服務器已經崩潰或目的地不可達,但可能須要比較長的時間; 若是服務器在客戶端發現崩潰前重啓,服務器的TCP丟失了崩潰前的全部鏈接信息,因此服務器TCP對接收的客戶數據分節以RST響應。 進程
2、關於socket的recv:
對於TCP non-blocking socket, recv返回值== -1,可是errno == EAGAIN, 此時表示在執行recv時相應的socket buffer中沒有數據,應該繼續recv。
【If no messages are available at the socket and O_NONBLOCK is not set on the socket's file descriptor, recv() shall block until a message arrives. If no messages are available at the socket and O_NONBLOCK is set on the socket's file descriptor, recv() shall fail and set errno to [EAGAIN] or [EWOULDBLOCK].】
對於UDP recv 應該一直讀取直到recv()==-1 && errno==EAGAIN,表示buffer中數據包被所有讀取。
接收數據時常遇到Resource temporarily unavailable的提示,errno代碼爲11(EAGAIN)。這代表你在非阻塞模式下調用了阻塞操做,在該操做沒有完成就返回這個錯誤,這個錯誤不會破壞socket的同步,不用管它,下次循環接着recv就能夠。對非阻塞socket而言,EAGAIN不是一種錯誤。在VxWorks和 Windows上,EAGAIN的名字叫作EWOULDBLOCK。其實這算不上錯誤,只是一種異常而已。
} if (errno == EINTR) continue; return res; |
外記:
accetp()是慢系統調用,在信號產生時會中斷其調用並將errno變量設置爲EINTR,此時應從新調用accept()。
因此使用時應這樣:
while(1) { |
signal 與 sigaction 區別:
signal函數每次設置具體的信號處理函數(非SIG_IGN)只能生效一次,每次在進程響應處理信號時,隨即將信號處理函數恢復爲默認處理方式.因此若是想屢次相同方式處理某個信號,一般的作法是,在響應函數開始,再次調用signal設置。
int sig_int(); //My signal handler
...
signal(SIGINT, sig_int);
...
int sig_int()
{
signal(SIGINT, sig_int);
....
}
這種代碼段的一個問題是:在信號發生以後到信號處理程序中調用s i g n a l函數之間有一個
時間窗口。在此段時間中,可能發生另外一次中斷信號。第二個信號會形成執行默認動做,而對
中斷信號則是終止該進程。這種類型的程序段在大多數狀況下會正常工做,使得咱們認爲它們
正確,而實際上卻並非如此。
另外一個問題是:在進程不但願某種信號發生時,它不能關閉該信號
sigaction:
1.在信號處理程序被調用時,系統創建的新信號屏蔽字會自動包括正被遞送的信號。所以保證了在處理一個
給定的信號時,若是這種信號再次發生,那麼它會被阻塞到對前一個信號的處理結束爲止
2.響應函數設置後就一直有效,不會重置
3.對除S I G A L R M之外的全部信號都企圖設置S A _ R E S TA RT標誌,因而被這些信號中斷
的系統調用(read,write)都能自動再起動。不但願再起動由S I G A L R M信號中斷的系統調用的緣由是但願對I / O操做能夠設置時間限制。
因此但願能用相同方式處理信號的屢次出現,最好用sigaction.信號只出現並處理一次,能夠用signal