java.io.IOException: Connection reset by peer

错误信息:

Caused by: java.net.SocketException: Connection reset by peer: socket write error
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
        at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:216)
        at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:487)
        at org.apache.coyote.http11.InternalOutputBuffer.flush(InternalOutputBuffer.java:120)
        at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:828)
        at org.apache.coyote.Response.action(Response.java:173)
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366)
        ... 90 more

错误原因:

从日志中可以看到是Socket套接字在write 数据时抛出了该错误。导致“Connection reset”的原因是服务器端因为某种原因关闭了Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示“java.net.SocketException: Connection reset”。

可能有同学对复位标志“RST”还不太了解,这里简单解释一下:

TCP建立连接时需要三次握手,在释放连接需要四次挥手;例如三次握手的过程如下:

  1. 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;

  2. 第二次握手:服务器收到syn包,并会确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

  • 服务器返回了“RST”时,如果此时客户端正在从Socket套接字的输出流中读数据则会提示Connection reset”;

  • 服务器返回了“RST”时,如果此时客户端正在往Socket套接字的输入流中写数据则会提示“Connection reset by peer”。

Socket默认连接60秒,60秒之内没有进行心跳交互,即读写数据,就会自动关闭连接。如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。如果一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。

简单的说就是在连接断开后的读和写操作引起的。

原因1

使用 Java NIO 建立 Socket 服务端,当客户端意外关闭的情况,不是发送指定指令通知服务器退出,就会产生此错误。

原因2

客户端再发起请求后没有等服务器端相应完,点击了stop按钮,导致服务器端接收到取消请求(频繁的刷新就会产生此问题)。通常情况下是不会有这么无聊的用户,出现这种情况可能是由于用户提交了请求,服务器端相应缓慢,比如业务逻辑有问题等原因,导致页面过了很久也没有刷新出来,用户就有可能取消或重新发起请求。 这种错误是合理范围内的,无法避免的,不必关心它。

原因3

Tomcat服务器在接受用户请求的时候,有其自身的处理能力,线程、服务器等各个资源限制,超出Tomcat承载范围的请求,就会被tomcat停掉,也可能产生该错误。服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭

原因4

Linux的线程机制会产生JVM出错的问题,特别是在连接高峰期间经常出现这样的问题,tomcat在linux下也出现类似情况。

原因5

如果网络连接通过防火墙,而防火墙一般都会有超时的机制,在网络连接长时间不传输数据时,会关闭这个TCP的会话,关闭后在读写,就会导致异常。 如果关闭防火墙,解决了问题,需要重新配置防火墙,或者自己编写程序实现TCP的长连接。实现TCP的长连接,需要自己定义心跳协议,每隔一段时间,发送一次心跳协议,双方维持连接。

解决办法:

1.出错了重试

这种方案可以简单防止“Connection reset”错误,然后如果服务不是“幂等”的则不能使用该方法

2.客户端和服务器统一使用TCP长连接

客户端和服务器统一使用TCP长连接:客户端使用TCP长连接很容易配置(直接设置HttpClient就好),而服务器配置长连接就比较麻烦了,就拿tomcat来说,需要设置tomcat的maxKeepAliveRequests、connectionTimeout等参数。另外如果使用了nginx进行反向代理或负载均衡,此时也需要配置nginx以支持长连接(nginx默认是对客户端使用长连接,对服务器使用短连接)。

使用长连接可以避免每次建立TCP连接的三次握手而节约一定的时间,但是我这边由于是内网,客户端和服务器的3次握手很快,大约只需1ms。ping一下大约0.93ms(一次往返);三次握手也是一次往返(第三次握手不用返回)。根据80/20原理,1ms可以忽略不计;又考虑到长连接的扩展性不如短连接好、修改nginx和tomcat的配置代价很大(所有后台服务都需要修改);所以这里并没有使用长连接。

3.客户端和服务器统一使用TCP短连接

客户端和服务器统一使用TCP短连接:我这边正是这么干的,而使用短连接既不用改nginx配置,也不用改tomcat配置,只需在使用HttpClient时使用http1.0协议并增加http请求的header信息(Connection: Close)

httpGet.setProtocolVersion(HttpVersion.HTTP_1_0);
httpGet.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);

 

文章参考:

https://my.oschina.net/xionghui/blog/508758

https://blog.csdn.net/testcs_dn/article/details/78707219

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心歌技术

打赏不能超过你的早餐钱!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值