Broken pipe vs Connection reset Exception
目錄
Broken pipe 和 Connection reset 什麼情況下會發生?
Broken pipe 和 Connection reset 什麼情況下會發生?
下面通過兩個不同的客戶端demo,以及一個服務端 demo 的驗證結果來說明
服務端demo
package com.study.hsyang.problems.brokenPipe; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args){ ServerSocket ss = null; try { System.out.println("Server start..."); ss = new ServerSocket(7342); Socket s = ss.accept(); InputStream is = s.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("recv:" + new String(buf, 0, len)); Thread.sleep(5000);//等待 5 秒,確保客戶端連線已經關閉了 try{ System.out.println("send hello1 start"); s.getOutputStream().write("hello1".getBytes()); System.out.println("send hello1 end"); }catch(IOException e) { e.printStackTrace(); } try{ System.out.println("send hello2 start"); s.getOutputStream().write("hello2".getBytes()); System.out.println("send hello2 end"); }catch(IOException e){ e.printStackTrace(); }try{ System.out.println("send hello3 start"); s.getOutputStream().write("hello3".getBytes()); System.out.println("send hello3 end"); }catch(IOException e){ e.printStackTrace(); } System.in.read();// block program } catch (Exception e) { e.printStackTrace(); } } }
大致邏輯:服務端在接收到客戶端傳送的 “hello” 之後休眠 5 秒,然後連續傳送三個字串至客戶端。
客戶端 Demo1:
package com.study.hsyang.problems.brokenPipe; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; public class Client { public static void main(String[] args) { try { System.out.println("Client start..."); Socket s = new Socket(); s.setSoLinger(true, 0);// 設定呼叫 close 就傳送 RST 報文 s.connect(new InetSocketAddress("172.31.6.41", 7342)); OutputStream os = s.getOutputStream(); os.write("hello".getBytes()); Thread.sleep(2000);//休眠2秒,確保服務端讀操作已經完成 s.close(); System.in.read();// block program } catch (Exception e) { e.printStackTrace(); } } }
大致邏輯: 在傳送完 “hello” 之後,會休眠兩秒,然後立刻異常關閉(直接向服務端傳送 rst 報文)。
先啟動服務端,然後啟動客戶端,至執行結束後服務端會丟擲如下異常
分析
通過 wireShark 捕獲的報文資訊
圖片每一行簡單說明:
1:客戶端向服務端傳送 【SYN】 報文,請求建立連線
2:服務端向客戶端傳送 【SYN,ACK】報文,同意建立連線,並再次向客戶端確認
3:客戶端向服務端再次傳送【ACK】報文,再次進行連線確認,至此。三次握手完成,tcp 連線正式建立成功
4:客戶端向服務端傳送 hello 報文
5:服務端傳送收到 hello 報文的響應
6:異常關閉 Close 連線(由於SO_LINGER設定為0的緣故
),向服務端傳送 【RST,ACK】報文。
由於客戶端異常關閉,且已經向服務端傳送了【RST,ACK】報文。在關閉後,服務端首次向客戶端傳送訊息,服務端會丟擲 Connection reset 異常,之後再次傳送訊息就會丟擲 Broken pipe 異常了。
客戶端Demo2
package com.study.hsyang.problems.brokenPipe;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
System.out.println("Client start...");
Socket s = new Socket();
s.connect(new InetSocketAddress("172.31.6.41", 7342));
OutputStream os = s.getOutputStream();
os.write("hello".getBytes());
Thread.sleep(2000);//休眠2秒,確保服務端讀操作已經完成
s.close();
System.in.read();// block program
} catch (Exception e) {
e.printStackTrace();
}
}
}
demo2與demo1的唯一區別就是少了 SO_LINGER的設定,使得客戶端可以正常
close(
預設close()的行為是,如果有資料殘留在socket傳送緩衝區中則系統將繼續傳送這些資料給對方,等待被確認,然後返回)
服務端列印結果
分析
由服務端列印結果可知,在客戶端正常關閉之後,服務端向客戶端多次傳送訊息,也不會丟擲任何異常了
wireShark捕獲的報文
圖片中紅框位置為客戶端向服務端傳送的斷開連線請求。
總結
當第一次向已經異常關閉的通道寫入資料時,會丟擲 Connection reset Exception,再次寫入時會丟擲 Broken pipe Exception。
當向已經正常關閉的通道寫入資料時,不會丟擲任何異常
Thanks:
【你假笨@JVM】:http://lovestblog.cn/blog/2014/05/20/tcp-broken-pipe/
【永志●哥德】https://www.cnblogs.com/metoy/p/6565486.html