目录
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