网络修炼笔记:可靠传输与深入 TCP 三次握手

通过华为 eNSP + Wireshark 抓包实验,深度剖析 TCP 三次握手完整过程:标志位 Flags、序列号 Seq 与确认号 Ack 的加 1 联动机制,以及操作系统状态机转换。

...

实验环境与物理拓扑

  • 模拟器:华为 eNSP
  • 实验拓扑:Client(客户端) ↔ SW1(交换机) ↔ Server(服务器)

若Client和Server两台设备直接相连,数据抓包选项会呈灰色不可点击状态。因此,中间必须加一台二层交换机,此时抓取交换机与任意端连接的接口即可顺利开启 Wireshark。

  • 网络层(IP)规划
  • Client IP:192.168.1.1,子网掩码:255.255.255.0
  • Server IP:192.168.1.2,子网掩码:255.255.255.0
  • 应用层(Service)规划: Server 开启标准的 FTP(文件传输服务),默认监听 21 端口。

TCP 三次握手实验拓扑图


理论前置:TCP 报文头结构与核心控制位

标志位(Flags)—— 决定这个包的“性质”

TCP 头部有一组控制位(共 6 个或更多,占用 1 个字节左右的空间),它们就像开关一样,置为 1 表示启用:

  • SYN (Synchronize)同步标志位。当启动一个新连接时,此位必须置为 1。
  • ACK (Acknowledgment)确认标志位。除了第一个发起连接的包之外,后续所有在网上传输的 TCP 包,该位都必须置为 1,表示“确认号(Ack Number)有效”。

序号(Seq)与确认号(Ack)—— 决定数据的“顺序”

  • Sequence Number (Seq)序列号。由发送端随机生成的计数器,用来告诉接收端:“本报文段所发送的数据的第一个字节的编号是多少”。
  • Acknowledgment Number (Ack)确认号。接收端用来告诉发送端:“你之前发的包我安全收到了,我期望你下一次从哪个字节开始发起”。

实验配置与抓包步骤

服务器端配置:双击 Server → 切换到 ServerInfo 选项卡 → 点击左侧 FtpServer → 选择一个本地文件夹作为 FTP 根目录 → 点击 Start 启动。

Server 配置 FTP 服务并启动

开启抓包:右键点击交换机连接的物理接口,选择 开始抓包 激活 Wireshark。

交换机接口开启 Wireshark 抓包

客户端发起连接:双击 Client → 切换到 ClientInfo → 点击左侧 FtpClient → 在 Server Address 栏输入 192.168.1.2,Port 输入 21 → 点击 登录 按钮。

Client 连接 FTP 服务器

过滤报文:在 Wireshark 的过滤器中输入小写的 tcp,回车锁定最开始触发的三条报文。

Wireshark 过滤 TCP 三次握手报文


深度解剖:三次握手时序、数据对齐与状态机转换

以下为真实实验环境下捕获到的三次握手核心报文完整数据:

1
2
3
27  56.891000  192.168.1.1  192.168.1.2  TCP  58  2055 → 21 [SYN]      Seq=0 Win=8192 Len=0
28  56.906000  192.168.1.2  192.168.1.1  TCP  58  21 → 2055 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0
29  56.906000  192.168.1.1  192.168.1.2  TCP  54  2055 → 21 [ACK]      Seq=1 Ack=1 Win=8192 Len=0

第一次握手(No. 27 报文)

  • 方向:Client(192.168.1.1) → Server(192.168.1.2
  • 报文核心字段Flags = [SYN], Seq = 0, Ack = 0
  • 操作系统状态转换:Server 本来在 LISTEN(监听);Client 把包发出去后,自身状态瞬间切换为 SYN_SENT(同步已发送)。

“服务器你好,我是 2055 端口。我想和你建立连接,这是我发给你的第一个包,我的编号是 0,收到请回答。”

第二次握手(No. 28 报文)

  • 方向:Server(192.168.1.2) → Client(192.168.1.1

  • 报文核心字段Flags = [SYN, ACK], Seq = 0, Ack = 1

  • 状态转换:Server 收到请求后,在系统底层开辟一块临时缓存,状态切换为 SYN_RCVD(同步已接收)。

  • 数字逻辑

  • Ack = 1 的由来:Server 将客户端发来的 Seq = 0 执行了 加 1 操作(0+1=1),通过 Ack = 1 明确告诉客户端:“你编号为 0 的包我拿到了,接下来我管你要编号为 1 的包。”

  • Seq = 0 的由来:这是服务器自己作为一个独立的发送端,生成属于服务器侧的初始序列号。

“客户端你好,我收到你的申请了,我同意。你刚刚给我的 0 号包我已经盖章确认了,下次请发 1 号包。同时,这也是我发给你的第一个包,我自身的编号是 0。”

第三次握手(No. 29 报文)

  • 方向:Client(192.168.1.1) → Server(192.168.1.2

  • 报文核心字段Flags = [ACK], Seq = 1, Ack = 1

  • 操作系统状态转换:Client 收到服务器的同意答复,认为双向通信没问题,状态到最高级:ESTABLISHED(连接已建立)。当这个包送到 Server 后,Server 的状态也同步变成 ESTABLISHED。

  • 数字逻辑

  • Seq = 1 的由来:完美响应了上一步服务器索要 Ack = 1 的请求。

  • Ack = 1 的由来:Client 将服务器发来的 Seq = 0 执行了 加 1 操作(0+1=1),告诉服务器:“你刚才发给我的服务器 0 号包我也拿到了,下次请发 1 号包。”

“服务器,你的同意答复我收到了!瞧,这就是你管我要的 1 号包(Seq=1)。你刚刚给我的服务器 0 号包我也盖章确认了。握手圆满结束,我们开始干正事传文件吧!”


结论

握手的核心是同步序号:三次握手不仅是打招呼,最重要的是在物理不可靠的网路上,通过 Seq 和 Ack 的加 1 联动,在两端同步一个绝对不会乱序的“数据计数器”。 Flags 是命令,Seq/Ack 是数据:[SYN] 表示申请建立连接,[ACK] 表示收到。在三次握手阶段,数据长度(Len=0)均为 0,因为此时还没有开始真正传输文件。

Created by aka.g