这两个异常在网络编程里应该是比较常见的,之前对于他们的理解只知道是对端关闭了连接,但什么情况下会报Broken pipe,什么情况下会报Connetion reset by peer是一无所知的,今天专门就这个问题做一个调研,下面记录下整个调研过程。
TCP的连接的建立与终止
在讲具体原因之前,有必要说一下TCP的三次握手与四次挥手,下面用一张图来说明(图是网上找的)。

上面三条是连接的建立,也就是说的三次挥手;下面四条是正常的连接终止,也就是四次挥手,关于为什么建立连接需要三次交互,而终止连接需要四次交互,这里不做过多解释,可以查询相关信息。正常关闭连接是发送FIN,还有一种异常关闭是发送RST,今天要讨论的就和这个有关系。
异常模拟
下面写了一段NIO的代码,Server端代码如下,代码很简单,看不懂的可以稍微补一点Nio的知识:
1 |
import java.io.IOException; |
大家可以看到,在接收到客户端的数据以后,首先会往客户端写数据,在@1代码处,然后会再次往客户端写数据,在@2代码处。下面再看一下客户端的代码:
1 |
import java.io.IOException; |
当客户端建立完连接往服务端发送数据后,会调close()方法,关闭连接,在代码@4处。我们先启动NioServer,然后再启动NioClient。会发现NioServer会报如下异常:
1 |
[[email protected] tmp]# java NioServer |
而此时,我们用tcpdump抓包看的情况如下:
1 |
17:35:38.819801 IP 192.168.2.11.33962 > 192.168.2.60.cslistener: Flags [S], seq 3601143964, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 9], length 0 |
结合上面信息,我们可以看到,在NioClient调用close()方法后,NioClient会往NioServer发送一个FIN包,而NioServer还继续往NioClient写数据,此时会收到NioClient返回的RST;接着NioServer继续往NioClient写数据,这个时候,NioServer就报了Broken pipe;
现在我们将NioClient代码处@3的注释打开,然后执行同样的操作,会发现NioServer报如下异常:
1 |
[[email protected] tmp]# java NioServer |
而此时,我们用tcpdump抓包看的情况如下:
1 |
17:37:12.234530 IP 192.168.2.11.34818 > 192.168.2.60.cslistener: Flags [S], seq 2138229963, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 9], length 0 |
结合上面信息,我们可以看到,在NioClient调用close()方法后,因为配置了soLinger选项,也即,NioClient会往NioServer发送一个RST包,而NioServer还继续往NioClient写数据,这个时候,NioServer就报了Broken pipe。
结论
从上面的两个例子可以看出,对于被动关闭方来说,也就是上述例子的NioServer,有两种情况:
被FIN关闭
第一次write得到了RST响应,但不会抛异常,第二次write,就会报Broken pipe(针对Linux系统)
被RST关闭
无论读写都会报Connection reset by peer
RST的场景
什么场景下会发送RST呢,网上总结的也很多,这里就不在赘述了。




近期评论