大家可以先读下下面两篇,写的也比较清楚了。我再做总结扩展。
https://www.yisu.com/zixun/218302.html
http://lovestblog.cn/blog/2014/05/20/tcp-broken-pipe/
收到对端的RST响应,程序会返回ECONNRESET错误码。比如对方是TCP主动关闭方(.close()),进入FIN_WAIT2之后,再向对端发数据会收到RST
EPIPE向收到过RST的对端再次读写数据会立刻收到SIGPIPE信号并返回EPIPE。
SIGPIPE默认是会中止程序的,所以程序一般都捕获无视掉了,jvm也是。
两者定义处,c标准库erro_list.c
615行可见 Connection reset by peer 字符串
360行可见 Broken pipe 字符串
说明这俩是系统层面的,所以不止在jdk,你可能在任何有用到socket通讯的组件比如redis、mq甚至ssh窗口看到这个异常
open jdk8_b120
SocketInputStream
上图为jni socketRead0()函数处理异常码的代码段,其中JNU_ThrowByName函数会立刻抛出给定类型的异常,异常的message为第三个参数
可见,对于读操作,ECONNRESET或EPIPE都直接抛出了 sun.net.ConnectionResetException。此后,java对其又做了捕获包装:
java侧会捕获sun.net.ConnectionResetException,抛出 java.net.SocketException,message还是Connection reset。
同样,上图为 socketWrite0() 函数,对于ECONNRESET jni还是会抛出sun.net.ConnectionResetException,java同样也会吃掉,抛出message一样类型变掉的异常:
上图可见,Java侧其他SocketException在本侧socket未关闭时会直接抛出,而Jni侧会通过NET_ThrowByNameWithLastError函数处理:
如果系统异常码有对应字符串,则使用异常码字符串,类型还是传入的类型,即最后会抛出 java.net.SocketException(“Broken pipe”)
搜索源码,发现还有PlainSocketImpl里avaliable0()会有对ECONNRESET的特殊处理,这个类是socket的具体实现。不过jdk默认已经不用这个实现了,而是NioSocketImpl:
通过第二节发现,抛的异常信息要么是Connection reset,要么是Broken Pipe,那Connection reset by peer是哪里抛出来的?
显然,除了指名处理这两个异常码的地方,有未指名,按默认逻辑抛出的地方,比如
这是nio对fd做实际读写的地方,自然也有jni:
这里 return 行的 read、write就是linux经典的系统调用函数了,我们再来看它如何处理返回值:
对于异常码,除了EAGAIN和EINTR,其他没有特殊处理,直接抛出IOException,看红框函数名大家应该都能猜到这个是做什么的:
也就是说有错误码对应的系统异常字符串,就抛出 IOExcetion + 异常码对应字符串,比如Conenction reset by peer,没有则才根据读写抛出 Read / Write failed 信息的IOException(这里message不会带errno,是个坑的地方)
- Connection reset 是由java捕获jni包装的系统异常后抛出的SocketException,出现的位置:
a. 读操作,SocketInputStream,系统返回ECONNRESET或EPIPE
b. 写操作,SocketOutputStream, 系统返回ECONNRESET - Connection reset by peer 是由jni捕获系统ECONNRESET抛出的IOException,一般而言能说明系统用到了如nio的其他通讯框架
- Broken pipe 一定由EPIPE引起,可能是由系统层面抛出的IOException,也可能是jdk包装后的SocketException,出现的位置:
a. 写操作,SocketOutputStream,SocketException
b. FileDispatcherImpl read或write抛出的,IOException
…



