1. 程式人生 > >Broken pipe vs Connection reset Exception

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