
一、先回顾 TCP 头部关键字段(和四次挥手相关的核心字段)
| 字段名 | 作用(四次挥手中的意义) | 补充说明 |
|---|---|---|
| FIN | 标记 "数据发送完毕,请求关闭连接" | 1 表示请求关闭,0 表示正常数据 |
| ACK | 标记 "确认号有效",用于确认收到对方的数据 | 1 表示确认,0 表示不确认 |
| seq | 发送方的 "序列号",标识本端发送的数据位置 | 随数据递增,保证有序性 |
| ack | 接收方的 "确认号",期望对方下一个序列号 | 一般为 "收到的 seq + 1" |
二、四次挥手每一步的字段解析(结合流程)
1. 第一步:A→B,A 发 FIN 报文
- 字段内容 :
FIN=1,ACK=0(默认,若之前有数据会带 ACK,但这里是第一次挥手,主要发 FIN ),seq=u,ack=任意(因为还没收到 B 新数据,ack 无意义) - 为啥发这些?
FIN=1:告诉 B "我(A)没数据要发了,请求关闭 A→B 方向的连接"。seq=u:A 本次发送的 "序列号",是 A 之前数据序列号的延续(比如 A 之前发数据的最后 seq 是u-1,现在发 FIN 用seq=u,占一个序列号)。ACK=0:这里主要是发 FIN,没有需要确认的新数据(如果之前有未确认的数据,ACK 会置 1 并带 ack 号,但第一次挥手时,假设之前数据已确认,所以 ACK 可置 0 或 1,不影响核心逻辑,实际实现可能默认带 ACK=1,需看具体场景)。
2. 第二步:B→A,B 发 ACK 报文
- 字段内容 :
ACK=1,ack=u+1,seq=v,FIN=0 - 为啥发这些?
ACK=1:告诉 A "我(B)确认收到你的 FIN 报文了"。ack=u+1:B 期望 A 下一个序列号是u+1(因为 B 收到了 A 的seq=u,所以确认号是u+1,表示 "我收到了 seq≤u 的所有数据,下一个要 u+1")。seq=v:B 自己的 "序列号",延续 B 之前发送数据的 seq(比如 B 之前最后发的 seq 是v-1,现在发 ACK 用seq=v,即使没带数据,序列号也要递增,因为 TCP 是面向字节流的,每个报文(包括控制报文)都占序列号)。FIN=0:B 还没准备好关闭 B→A 方向的连接(因为可能还有数据要发),所以 FIN 置 0。
3. 第三步:B→A,B 发 FIN 报文
- 字段内容 :
FIN=1,ACK=1,ack=u+1,seq=w - 为啥发这些?
FIN=1:告诉 A "我(B)也没数据要发了,请求关闭 B→A 方向的连接"。ACK=1:继续确认 A 的 FIN(因为 TCP 要求,只要发确认,ACK 就置 1,这里同时确认之前的ack=u+1)。ack=u+1:和第二步一样,确认 A 的seq=u,保持确认号不变(因为 A 没有新数据发送,确认号一直是u+1,直到 A 有新动作)。seq=w:B 发送 FIN 报文的序列号,延续 B 之前的 seq(比如 B 第二步发的seq=v,处理完数据后,现在发 FIN 用seq=w,w是v之后的序列号,因为 B 可能在第二步和第三步之间发了数据,seq 会递增;如果没发数据,w = v + 1,因为 ACK 报文虽然没带数据,但占了序列号v,所以下一个 seq 是v + 1)。
4. 第四步:A→B,A 发 ACK 报文
- 字段内容 :
ACK=1,ack=w+1,seq=u+1,FIN=0 - 为啥发这些?
ACK=1:告诉 B "我(A)确认收到你的 FIN 报文了"。ack=w+1:A 期望 B 下一个序列号是w+1(因为 A 收到了 B 的seq=w,所以确认号是w+1)。seq=u+1:A 发送 ACK 报文的序列号,延续 A 之前的 seq(A 第一步发的seq=u,第二步收到 B 的 ACK 后,A 的 seq 不需要递增,因为 A 没有新数据发送;但第四步发 ACK 时,seq 是u+1,因为 A 第一步的 FIN 占了seq=u,所以下一个 seq 是u+1,即使没带数据,序列号也要推进)。FIN=0:A 已经发过 FIN 了,这里只是确认 B 的 FIN,所以 FIN 置 0。
三、字段值的 "为啥是这些"(核心逻辑)
-
序列号(seq):
- TCP 是 面向字节流 的协议,每个字节都有编号,
seq标识 "本端发送的当前报文的第一个字节的序号"。 - 即使是控制报文(如 FIN、ACK),也会占用序列号(因为要保证 "有序性" 和 "不丢包")。所以每次发报文(不管有没有数据),
seq都会递增,延续之前的序列。
- TCP 是 面向字节流 的协议,每个字节都有编号,
-
确认号(ack):
ack是 "期望对方下一个发送的序列号",规则是ack = 收到的 seq + 1。- 作用是告诉对方:"我已经收到了
seq及之前的所有数据,你下次发seq + 1吧",保证数据不重复、不丢失。
-
FIN 与 ACK 标记:
FIN是 "关闭连接请求",置 1 表示 "本端没数据要发了,请求关闭方向"。ACK是 "确认有效",置 1 表示 "本端确认收到了对方的报文(通过ack号体现)"。- 四次挥手中,ACK 几乎全程置 1(除了第一步可能置 0,但实际实现常带 ACK=1),因为每次回应都需要确认对方的报文;FIN 则在需要关闭方向时置 1。