前言
TCP三次握手(TCP 3-Way Handshake)稍微学过一点网络知识的人都会念。但如此一个抽象的概念具象化后是什么样?本文就以Wireshark抓包结果,展示开始、结束TCP会话的实际过程。
从实际案例观察
下图为一次完整的TCP会话。会话的发起、结束均由客户端完成。
- 客户端:192.168.31.10
- 服务器:192.168.100.1
一、发起会话
从上图No.97~102可以看到发起TCP会话时的三次握手过程:
| 第N次握手 | 发起 | 阶段(Flags) | SEQ | ACK |
|---|---|---|---|---|
| 1 | Client | SYN | x=0 | 无 |
| 2 | Server | SYN, ACK | y=0 | x+1=1 |
| 3 | Client | ACK | x+1=1 | y+1=1 |
🤔为什么Wireshark中看到的SYN包显示Seq=0?
每个TCP连接必须使用一个“初始序列号(ISN)”,并且这个ISN应该随时间变化。——RFC793
真实的Seq序列号是随机的。为了让你更容易看懂握手过程,Wireshark有一个“相对序列号(Relative Sequence Numbers)”功能。它会把:
- 第一个SYN的Seq显示为0
- 第二个SYN/ACK的Seq显示为0
二、结束会话
图中No.111~121展示了客户端结束TCP会话的过程(四次握手):
| 第N次握手 | 发起 | 阶段(Flags) | SEQ | ACK |
|---|---|---|---|---|
| 1 | Client | FIN, ACK | 1864 | 2407 |
| 2+3 | Server | FIN, ACK | 2407 | 1864 |
| 4 | Client | ACK | 1865 | 2408 |
| 非必须 | Server | ACK | 2408 | 1865 |
注意:该例中第二、三次握手,服务端将ACK(确认客户端FIN)与FIN(主动关闭)合并在同一个包中发送(piggyback)。一般的流程应该是:
- Client — [FIN, ACK] –> Server
- Client <– [ACK] — Server
- Client <– [FIN, ACK] — Server
- Client — [ACK] –> Server
此外,结束会话亦可由服务端主动发起(如超时、服务端特别设置等)。过程就是把上表中Client和Server反过来。
附录:各个参数的变化规律
序列号SEQ
- SEQ从初始值开始,默默记录自己发送的累积字节数。
| 发送内容 | 增加量 |
|---|---|
| SYN/FIN | +1(等价1字节) |
| ACK/RST | +0 |
| 其他数据 | +数据字节数 |
ACK
- ACK = 接收包的SEQ + 接收包的字节数(SYN/FIN按1算)
- 用人话说就是:
我期望收到你下一个字节的序号。
请注意:ACK的期望对SEQ并没有什么卵用。SEQ的序号不会受收到的ACK影响。
从上面描述可以看出,利用TCP会话中最后的SEQ、ACK数值,我们可以估算出某一方向向对方发送的总字节数,即对方最后的ACK - 自己初始的SEQ。但若存在SACK、乱序或重传,该方法会失效,需要结合Seq/Ack时间线分析。

