一次诡异的TCP连接异常分析
近期测试发现,项目中的ProxySvr从DatStore模块下载数据偶现失败,从返回的错误码来看,框架层提示网络错误,数据下载都是内网传输,带包ping测试rtt、丢包率正常,网络出问题的机率很小。
这种网络问题,通常就需要使用抓包工具tcpdump进行抓包来辅助分析,在ProxySvr上抓包分析来看,直接原因是proxySvr 收到了RST包,导致下载数据失败。
回想起网络相关知识,TCP是一个全双工的网络协议,建立连接要三次握手、断开连接要四次挥手,在TCP连接中产生RST段标识的场景主要有以下几个方面。
- 服务方端口未监听,客户端主动连接时,会收到一个rst段标识。
- 网络链接被拦截,例如目标主机、网络路径中防火墙未开放端口,导致数据包无法继续传输。
- socket提前关闭,socket 接收缓冲区数据未被完全读取,就调用close函数,会向发送端发送rst端标识 。
- 向已经关闭连接的服务端发送数据,服务端主动调用close关闭连接以后,客户端未正常处理FIN请求,继续发送数据,这个时候就会收到RST段标识。
根据日志分析,属于偶现问题直接可排除服务放端口未监听、防火墙问题,后面两种场景,就需要使用Wireshark解析抓包文件,分析网络链路上产生的问题。在Wireshark里面点击鼠标右键就可以直接追踪 tpc流,可以看到整个TCP连接的建立、数据传输、连接释放过程。
从抓包文件来看,这次连接经历了以下过程。
- 客户端过三次握手与服务端建立TCP连接,连接建立成功。
- 客户端发送了一个OPTIONS请求,服务端回复了一个200 OK,客户端回复了一个ACK。
- 服务端发送了一个FIN,客户端回复了一个ACK,但没有发FIN,四次挥手没能成功。客户端没有处理此次服务端主动断链接请求。
- 客户端继续向服务端发送HTTP GET请求,服务端回复了一个RST标识段。
根据抓包文件的分析,很容易得到HTTP GET 请求失败的原因是客户端继续使用了一个服务端已经关闭的TCP链接,基本符合上述的收到RST标识段场景的第四点。需要从业务的角度,进一步深入挖掘背后的原因。
ProxySvr会向服务端发送OPTIONS请求,用于上报监控字段,此信令服务端仅支持短连接模式。
ProxySvr向服务端发送GET请求,用于下载数据,此信令服务端支持长链接模式。
ProxySvr使用网络框架来实现数据的收发功能,默认使用长链接模式、可以配置成短连接。
从业务流程来看,直接原因就是客户端网络框架对于OPTION请求、数据下载请求使用了同一个TCP长链接,并且网络框架在收到服务端主动断链接的FIN后,没有及时断开连接。解决此问题,可临时通过修改配置将TCP连接改成短连接,既可规避此问题。
仔细想来,最根本的原因还是框架对于TCP连接的处理存在异常,合理的实现方法应该是采用如下策略。
- 对于网络框架而言,无论本次连接是长链接还是短链接,收到服务端发送FIN标识,客户端就应该调用close函数关闭本次连接,反馈到应用层或者输出异常日志。
- 应用层在使用网络框架时,要考虑发送的请求类型以及框架层网络连接策略,通常网络框架会使用长链接来优化连接耗时、减少服务端端口占用,要结合业务场景调整连接策略。
对于网络连接类异常问题,如果业务层无法发现问题所在,优先考虑通过抓包分析连接的建立、释放过程,follow tcp 流分析TCP连接每一步是否符合预期,基本都能分析出连接异常的原因。
欢迎关注个人公众号,获取最新文章。