现在有一个场景,假设机器 A 启动了一个 TCP Server,机器 B 启动了一个 TCP Client,并且 TCP Client 与 TCP Server 创建好了连接。此时将 A 的网线拔掉会出现什么情况呢?为了先得出直观的结构,可以先做一个简单的实验测试一下

1. 当 TCP 链路创建好拔掉网线

  1. 在机器 A 与机器 B 都启动 tcpdump 进行抓包

  2. 机器 A 启动 TCP Server 程序。机器 B 启动 TCP Client 程序,并成功连上机器 A 的 TCP Server。

    此时用 netstat 分别在两台机器上面都可以看到该 TCP 链路处于 ESTABLISH 状态

  3. 机器 B 的 TCP Client 发送消息给机器 A 的 TCP Server

    机器 A 的 TCP Server 可以成功接收到机器 B 发送过来的消息

  4. 断开机器 A 的网线,查看链路状态

    此时用 netstat 分别在两台机器上面都可以看到该 TCP 链路仍处于 ESTABLISH 状态

    断开链路后机器 B 链路状态

    断开链路后机器 A 链路状态

  5. 机器 B 的 TCP Client 发送消息给机器 A 的 TCP Server

  6. 打开机器 A 的抓包可以看到在链路断开之后发送的那条 TCP 消息在不断的重传

    机器B重传

从这里可以看出 TCP 的链路状态,只是表示 TCP 的会话状态并不代表底层的数据链路状态,即使底层链路断开了,在 TCP 层没有收到通知的情况下,链路状态还是 ESTABLISH。

2. 恢复连接,机器 B 以相同的 IP 与端口向机器 A 发起连接

在完成上面那些操作之后,机器 A 的 TCP Server 还在监听,并且链路还是是 ESTABLISH 状态。此时退出机器 B 的 TCP Client 程序并插上机器 A 的网线,然后 B 绑定与之前相同的IP与端口向 A 建立 TCP 连接,此时会发送什么呢?

恢复连接,机器 B 以相同的 IP 与端口向机器 A 发起连接

通过抓包可以看到,机器 B 首先按照正常三次握手流程发送 SYN,但是 A 返回了一个异常的 ACK,然后 B 发送一个 RST 结束原来的链路。在结束完旧的链路之后B 再次发送 SYN 走三次握手的流程重新建立链路。需要注意的是这个过程对于 B 应用层是不可见的,应用只知道 connect 返回的结果是正确的。

附件

  1. [Machine A](https://blog.dongliu.site/upload/2024/11/Machine A.pcap)
  2. [Machine B](https://blog.dongliu.site/upload/2024/11/Machine B.pcap)

参考

  1. 拔掉网线后,原来的额 TCP 连接还存在吗
  2. Application control of TCP retransmission on Linux