Skip to content

4.9 已建立连接的 TCP,收到 SYN 会发生什么?

一个已经建立的 TCP 连接,客户端中途宕机了,而服务端此时也没有数据要发送,一直处于 Established 状态,客户端恢复后,向服务端建立连接,此时服务端会怎么处理?

TCP 连接是由「四元组」唯一确认的。

然后这个场景中,客户端的 IP、服务端 IP、目的端口并没有变化,所以这个问题关键要看客户端发送的 SYN 报文中的源端口是否和上一次连接的源端口相同。

  • 如果客户端的SYN报文的端口和历史连接不同
    • 服务器会认为时新的连接,重新三次握手建立连接
    • 旧的连接会一直保留,直到服务器主动发送数据或者通过TCP保活机制检测到客户端没有存活了
  • 如果客户端的SYN报文的端口和历史连接相同
    • 由于每次新建TCP连接第一次握手的SYN报文的序列号属于随机生成的,所以当前的SYN报文的序列号也是随机生成的,发到服务端
    • 之后,服务端由于保持了上一次连接的信息,因此ACK报文会继续使用上一次的确认号(SYN报文没有数据,且对于服务端不是建立连接阶段,所以ACK号不会+1),这个ACK称为 challenge ACK
    • 那么客户端收到的ACK号不是自己想要的,会回复 RST 报文,服务端收到之后,就会释放掉该连接

如何关闭一个 TCP 连接?

  • 直接杀掉进程

    • 如果杀掉客户端进程,客户端内核会发送 FIN 报文,断开这个进程与服务端的所有TCP连接,只影响当前进程
    • 如果杀掉服务端进程,服务户端内核会尝试发送 FIN 报文断开所有的 TCP 连接,服务端无法继续提供访问服务。
  • 伪造四元组相同的RST报文

    • 要伪造一个能关闭 TCP 连接的 RST 报文,必须同时满足「四元组相同」和「序列号是对方期望的」这两个条件。
    • 可以先通过伪造一个四元组相同的SYN报文,拿到合法的序列号(服务端会回复上面的challenge ack,有正确的序列号)
    • 然后用这个确认号作为 RST 报文的序列号,发送给服务端,此时服务端会认为这个 RST 报文里的序列号是合法的,于是就会释放连接!
    • linux中有killcx工具实现这种功能,会分别给客户端和服务端发送RST报文,释放双方的连接,如下图
      • tcpkill 工具也可以做到,不过tcpkill 工具是在双方进行 TCP 通信时,拿到对方下一次期望收到的序列号,然后将序列号填充到伪造的 RST 报文,并将其发送给对方,达到关闭 TCP 连接的效果。是被动获取,用于关闭活跃的 TCP 连接。如果非活跃的连接,无法获取报文
      • 而killcx是主动获取,可以关闭活跃和非活跃的连接

正在精进