为什么需要握手#
网络上两台电脑要可靠地传数据,第一步是确认「双方都能发、也都能收」。 这件事听起来简单,但在只有单向不可靠信道的前提下,需要三次交互才能确定。
用一个生活场景类比:你给同学打电话。
- 第一次握手:你拨号,同学电话响了 —— 他知道了「你的耳朵和嘴巴都好使」
- 第二次握手:同学接起来说「喂」 —— 你知道了「他的耳朵和嘴巴都好使」,他也知道了「你听得见」
- 第三次握手:你说「听到了,开始聊」 —— 他知道了「你确实听得到」
到这一步,双方才同时确认了四件事:自己能发、自己能收、对方能发、对方能收。
报文里的关键字段#
| 字段 | 作用 | 握手时怎么变 |
|---|---|---|
| SYN | 请求建立连接 | 第 1、2 次置 1 |
| ACK | 确认收到 | 第 2、3 次置 1 |
| seq | 自己的序号 | 随机生成,防止旧连接串味 |
| ack | 期望对方下次发的序号 | 等于对方 seq + 1 |
为什么是三次而不是两次#
这是考试的高频考点,也是理解的关键。
假设只有两次握手,那么服务端一收到 SYN 就认为连接建立了。问题在于: 网络中会滞留一些「过期的连接请求」。如果客户端早就放弃了, 而服务端收到这个迟到的 SYN 后直接建连接,就会白白占用资源等着一个不会来的客户端。
第三次握手的作用,就是让服务端在真正开始工作前,再确认一次客户端还在。
记忆口诀:两次握手只能确认「对方能收」,三次才能确认「对方也想连」。
为什么要随机初始序号#
如果每次都从 0 开始编号,那么上一次连接里滞留在网络中的旧数据包, 可能被误认为这次连接的数据。随机化初始序号后,这种概率就变得可以忽略。
用代码观察握手#
想让同学亲眼看到握手过程,可以用 Python 抓包:
import socket
# 创建一个 TCP 客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 这一步的三方交互,就是三次握手
# 抓包时你会依次看到 SYN、SYN+ACK、ACK 三个包
client.connect(("www.example.com", 80))
print("连接已建立,三次握手完成")
client.close()
配合 Wireshark 过滤 tcp.flags.syn == 1,就能在课堂上实时演示。
课堂思考题#
- 如果第三次握手的 ACK 丢了,服务端会怎么办?
- 为什么初始序号不直接用 0,而要用随机数?
- 四次挥手为什么比三次握手多一次?
小结#
| 次数 | 方向 | 携带标志 | 真正确认了什么 |
|---|---|---|---|
| 1 | 客户端 → 服务端 | SYN | 服务端知道客户端能发 |
| 2 | 服务端 → 客户端 | SYN + ACK | 客户端知道双方收发都正常 |
| 3 | 客户端 → 服务端 | ACK | 服务端确认客户端仍在,连接可用 |