Rxsi Blog GameServer Developer

TCP连接异常断开分析

2022-03-31
Rxsi
TCP

TCP 连接异常断开分析

1. 进程 crash

每个进程都是对应了一个task_struct结构,里面保存有该进程申请并占用的 socket 描述符,而 socket 实际上是一种系统资源,因此是具有与进程不同的生命周期的。TCP 的连接信息是由内核维护,当进程崩溃之后,内核会回收该进程申请的资源。当进程宕机后,所有已建立的连接会发送 FIN 报文,进行四次挥手,因此本端和对端的 TCP 连接都可以被正常关闭。

2. 主机崩溃,或又迅速重启

如果主机直接崩溃,那么肯定是无法进行四次挥手的,不过这已经无关紧要,因为一旦崩溃那么当前计算机的所有系统资源都会被回收,也就包括了所有的 TCP 资源了。但是对于对端的 TCP 资源,则需要区分不同的情况:

  • 1) 非活跃的 TCP 连接且没有开启 keep_alive 保活机制:

    如果当前的 TCP 是非活跃的,这意味着在本端的 TCP 资源被释放之后,对端是无法通过数据包重发机制来探测到 TCP 连接已经断开的,而又没有开启 keep_alive 保活机制,那么此时对端的 TCP 资源就会一直存在。

  • 2) 非活跃的 TCP 连接有开启 keep_alive 保活机制:

    如果开启了保活机制,那么根据配置,在一定的时间后会自动断开 TCP 连接。

    相关内核参数有:

      tcp_keepalive_time = 7200 // 当2个小时内没有数据流动时,启动保活机制
      tcp_keepalive_intvl = 75 // 当启动保活机制后,每次检测的间隔为75秒
      tcp_keepalive_probes = 9 // 总共检测9次
    

    因此,至少经过 2小时11分15秒 才能够发现一个“死亡连接”,在创建 socket 时使用SO_KEEPALIVE选项即可开启保活机制

  • 3) 活跃的 TCP 连接且主机崩溃后无重启

    当一端发送的报文没有得到 ACK 回复时,就会根据内核相关配置的参数触发重传,达到一定次数后(达到了限定的timeout)关闭 TCP 连接

    相关内核参数有:

      tcp_retries1 = 3
      tcp_retries2 = 15
      // 以上两个参数并不是限定重传的次数,而是结合初始RTO,计算timeout时间上限。当重传的时间间隔(以2的倍数倍增)超过该timeout参数,则中断TCP连接
    
  • 4) 主机崩溃之后又迅速重启

    重启之后的主机已经丢失了之前的 TCP 资源,那么如果接收到了对端 TCP 发送来的数据包,则会回复RST报文使对端回收掉旧的 TCP 资源。

    如果是重启之后的主机先向对端发送了第一次握手包SYN报文,此时本端充当的是客户端的角色,那么假设我们随机计算出的客户端端口号与之前的旧链接是一致的,即 TCP 的四元组信息都一致,那么该SYN报文将会被对端正处于 ESTABLISHED 状态的 TCP 接收。由于序列号不匹配,那么对端会回复一个当前期望接收到的正确序列号的ACK报文,而本端由于接收到这个ACK报文之后发发觉出对端的连接是异常的,因此会回复RST报文,使对端的旧链接释放。

    如果我们随机出的四元组与之前的不一致,那么可想而知会成功建立起一条新的连接,而旧链接的释放则又取决于其是否开启了keep_alive机制和链接是否活跃。


上一篇 分布式事务

Comments

Content