Skip to content

第四篇 TCP 连接的建立、状态和关闭

在前三篇中,我们已经掌握了字节流、四元组以及 TCP 首部等核心概念。本篇,我们将把这些静态的字段放进动态的时间轴里去考察:TCP 连接是如何同步双方的序列号(Sequence Number)的?为什么通信两端会处于不同的状态?全双工的两个发送方向是如何独立关闭的?以及,FIN、TIME_WAIT、CLOSE_WAIT 和 RST 这些状态或标志到底代表着什么含义?

我们将继续使用运行在 127.0.0.1:18080 上的 Python 脚本来做受控实验。在每个实验里,我们都会收集四个维度的“证据”来辅助分析:应用程序打印的 API 调用时间、操作系统(Windows 或 Linux)查询到的真实连接状态、Wireshark 抓取到的底层网络报文(Segment),以及根据 Seq/Ack 字段手工推演出的序列号变化。需要注意的是,由于本地回环网络(Loopback)通信速度极快,我们会用代码设置合理的暂停点,以便捕捉并观察那些转瞬即逝的中间状态。

学习路线

章节核心问题完成后的能力
第15章 三次握手双方如何同步初始序列号(ISN)能够逐包计算握手过程中的 Seq 与 Ack,并准确对应到应用层的 connectaccept 调用
第16章 TCP 连接状态机通信两端为何会显示不同的状态能够结合 API 调用、网络报文和计时器(Timer),完整推演出 TCP 的状态迁移过程
第17章 连接关闭与半关闭全双工通信的两个方向是如何独立结束的掌握 shutdown(SHUT_WR) 的正确用法,并能准确解释应用层 EOF 的含义
第18章 TIME_WAIT、CLOSE_WAIT 和连接释放连接结束后为何系统仍会保留状态能够判断当前的关闭状态究竟是正常的网络过渡、特定的编程模式,还是代码缺陷导致的资源泄漏
第19章 RST、异常断开和半开连接哪些底层事件会立即粗暴地中止连接能够清晰地区分连接拒绝、TCP 重置(Reset)、网络超时、主机失联以及半开连接(Half-Open)等异常场景

贯穿全篇的双向模型

理解 TCP 最清晰的方式,就是把一条 TCP 连接看作是两条独立的单向字节流

text
客户端发送方向  =============================>  服务端接收方向
客户端接收方向  <=============================  服务端发送方向

其中:

  • SYN 用于为这两个方向分别初始化序列号空间。
  • FIN 仅仅用来关闭发送该 FIN 报文的那一个单向通道(即“半关闭”)。
  • RST 则是一把快刀,会直接粗暴地中止整条双向连接。
  • 所谓的连接状态,本质上只是用来记录当前端点已经发送了哪些控制信息、以及收到了哪些控制信息。正是由于网络延迟和处理时差的存在,在同一时刻,客户端和服务端往往会处于完全不同的状态。

第四篇综合任务

本篇我们有一个核心的综合任务——亲自动手绘制五组典型的 TCP 状态时间线:正常连接建立、客户端主动触发半关闭、正常的双向彻底关闭、连接请求被直接拒绝,以及服务端强制发送 RST 重置连接。

对于每一组实验,都需要分别从客户端和服务端的视角,详细记录以下信息:

  1. 触发事件:比如应用层调用了 connectshutdownclose,或者是底层的网卡收到了网络报文,又或者是某个 TCP 计时器刚好到期。
  2. 状态变迁:该事件发生前后,本地操作系统的 TCP 连接状态发生了什么变化。
  3. 底层细节:对应网络报文的 TCP 标志位(Flags)、序列号(Seq)、确认号(Ack)以及负载(Payload)长度。
  4. 应用感知:应用程序在这一刻观察到了什么现象?是正常的函数返回值、读取到了 EOF 标志,还是抛出了某种网络异常?

请记住,时间线上的每一次状态迁移,都必须能找到直接的“触发证据”。另外,Wireshark 默认展示的“相对序列号”(Relative Sequence Number)非常便于直观阅读;但在进行手工推演和计算时,一定要搞清楚当前使用的是相对值,还是 TCP 首部中真实抓取到的绝对值(Raw Value)。

导航

Licensed under CC BY-NC-SA 4.0.