发送RST报文的几种可能的情况

您所在的位置:网站首页 rstack包 发送RST报文的几种可能的情况

发送RST报文的几种可能的情况

2024-06-02 11:28| 来源: 网络整理| 查看: 265

Connection reset 原因 接收到TCP-RST包

导致“Connection reset”的其中一个原因是服务器端因为某种原因关闭了Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示“java.net.SocketException: Connection reset”。

什么是复位标记RST:

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

第一次握手:客户端发送SYN包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认; 第二次握手:服务器收到SYN包,并会确认客户的SYN(ack=j+1), 同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1), 此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

可以看到握手时会在客户端和服务器之间传递一些TCP头信息,比如ACK标志、SYN标志以及挥手时的FIN标志等。

除了以上这些常见的标志头信息,还有另外一些标志头信息,比如推标志PSH、复位标志RST等。其中复位标志RST的作用就是“复位相应的TCP连接”。

TCP连接和释放时还有许多细节,比如半连接状态、半关闭状态等。详情请参考这方面的巨著《TCP/IP详解》和《UNIX网络编程》。

前面说到出现“Connection reset”的原因是服务器关闭了Connection[调用了Socket.close()方法]。 大家可能有疑问了:服务器关闭了Connection为什么会返回“RST”而不是返回“FIN”标志。原因在于Socket.close()方法的语义和TCP的“FIN”标志语义不一样:

发送TCP的“FIN”标志表示我不再发送数据了, 而Socket.close()表示我不在发送也不接受数据了。

问题就出在“我不接受数据” 上,如果此时客户端还往服务器发送数据,服务器内核接收到数据,但是发现此时Socket已经close了,则会返回“RST”标志给客户端。当然,此时客户端就会提示:“Connection reset”。

详细说明可以参考oracle的有关文档:http://docs.oracle.com/javase/1.5.0/docs/guide/net/articles/connection_release.html。

Connection reset

导致“Connection reset”的其中一个原因是服务器端因为某种原因关闭了Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示“java.net.SocketException: Connection reset”。

另一个可能导致的“Connection reset”的原因是服务器设置了Socket.setLinger (true, 0)。但我检查过线上的tomcat配置,是没有使用该设置的,而且线上的服务器都使用了nginx进行反向代理,所以并不是该原因导致的。关于该原因上面的oracle文档也谈到了并给出了解释。

Connection reset by peer

此外啰嗦一下,另外还有一种比较常见的错误“Connection reset by peer”,该错误和“Connection reset”是有区别的:

服务器返回了“RST”时,如果此时客户端正在从Socket套接字的输出流中读数据则会提示Connection reset”; 服务器返回了“RST”时,如果此时客户端正在往Socket套接字的输入流中写数据则会提示“Connection reset by peer”。 解决Connection reset几种方案

前面谈到了导致“Connection reset”的原因,而具体的解决方案有如下几种:

1. 出错了重试; 2. 客户端和服务器统一使用TCP长连接; 3. 客户端和服务器统一使用TCP短连接。

首先是出错了重试:这种方案可以简单防止“Connection reset”错误,然后如果服务不是“幂等”的则不能使用该方法;比如提交订单操作就不是幂等的,如果使用重试则可能造成重复提单。

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

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

正常情况为tcp四层握手关闭连接,而rst基本都是异常情况,使用 ping 可以看到丢包情况

RST复位标志发送原因几种可能整理如下:

被GFW拦截

防火长城(英文名称Great Firewall of China,简写为Great Firewall,缩写GFW),也称中国防火墙或中国国家防火墙,指中华人民共和国政府在其管辖因特网内部建立的多套网络审查系统的总称,包括相关行政审查系统。

对方端口未打开,发生在连接建立

全连接队列已满,发生在连接建立

当全连接队列已满就会根据tcp_abort_on_overflow策略进行处理。

Linux 可通过 /proc/sys/net/ipv4/tcp_abort_on_overflow 进行配置。

当tcp_abort_on_overflow=0,服务accept 队列满了,客户端发来ack,服务端直接丢弃该ACK,此时服务端处于【syn_rcvd】的状态,客户端处于【established】的状态。在该状态下会有一个定时器重传服务端 SYN/ACK 给客户端(不超过 /proc/sys/net/ipv4/tcp_synack_retries 指定的次数,Linux下默认5)。超过后,服务器不在重传,后续也不会有任何动作。如果此时客户端发送数据过来,服务端会返回RST。(这也就是我们的异常原因了)

当tcp_abort_on_overflow=1,服务端accept队列满了,客户端发来ack,服务端直接返回RST通知client,表示废掉这个握手过程和这个连接,client会报connection reset by peer。

close Socket 时recv buffer 不为空

例如,客户端发了两个请求,服务器只从buffer 读取第一个请求处理完就关闭连接,tcp层认为数据没有正确提交到应用,使用rst关闭连接。

移动链路

移动网络下,国内是有5分钟后就回收信令,也就是IM产品,如果心跳>5分钟后服务器再给客户端发消息,就会收到rst。也要查移动网络下IM 保持



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3