Skip to content

三次握手不是仪式,也不是为了显得专业。就一件事:通话开始前,双方都要亲自确认——「我能把话送到你那儿,你也能把话送到我这儿」。

少一次,有人会瞎等;多一次,没必要。

先把场面想成打电话 ​

你给对面打电话,要确认两件事:

  1. 对面能听见你
  2. 你能听见对面

所以实际会这样:

  1. 你:「喂,听得到吗?」
  2. 对面:「听得到。你听得到我吗?」
  3. 你:「听得到,开聊。」

第一次只证明了:你能发,对面能收。 第二次才补上:对面能发,你的耳朵也成。 第三次是对面要的那张回执:它那句「你听得到我吗」确实被你听见了。

两次打完,对面其实还不确定你听见没有。它不敢开始倒数据。

换成 TCP 包,就是这三下 ​

  1. 客户端发 SYN,带上自己的起始序号 j。意思:我要从 j 开始跟你说话,你在吗。自己进入 SYN_SENT。
  2. 服务端回 SYN+ACK,确认号 j+1,再带上自己的起始序号 k。意思:你的 j 我收到了,下一个我等 j+1;我也要从 k 开始说。自己进入 SYN_RECEIVED。
  3. 客户端再发 ACK,确认号 k+1。意思:你的 k 我也收到了。两边进入 ESTABLISHED,这才开始传数据。

确认号永远是「对方序号 + 1」,翻译成人话就是:下一包我要收这个号。

序号是各自随便挑的起始编号,后面每个字节都按这个号往下排。握手的另一半工作,就是把两边的号对上。号对不上,后面的数据谁也拼不回来。

一次行不行 ​

不行。你喊了一嗓子就开倒数据,对面可能根本不在,或者没听见。你自己也拿不到对面的起始序号,后面的编号对不上。

两次行不行 ​

不行。两个坑。

坑 1:网上会有「迟到的旧请求」 ​

网络会把包弄丢、弄晚。可能发生这种蠢事:

  1. 你发出一个 SYN,在路上堵着了。
  2. 你不耐烦,又发了一个,这次连上了,聊完了,挂了。
  3. 那个旧 SYN 这才晃到服务端。
  4. 服务端一看:有人要连我。回一个 SYN+ACK,并以为连接已经建好,开始空等数据。

你这边这笔连接早就结束了,不会再理它。服务端就在那儿傻等。

有第三次就不一样:服务端必须再听到你回一句 ACK,才把连接当真。迟到的那单,你根本不会回第三句,服务端等一会儿就放弃。

坑 2:对面那句「我听到了」可能丢 ​

两次握手时,服务端的 SYN+ACK 在路上丢了:

  • 你以为连上了,开始倒数据
  • 服务端以为还没连上(它的确认你没收到)
  • 你倒过去的数据,服务端不当连接,直接扔掉,继续等确认
  • 两边各等各的,卡死

第三次 ACK 就是把这层不确定钉死:服务端听到这句,才知道「对方确实收到了我的序号」。

为什么三次就够,不用四次 ​

三次之后:

  • 客户端:能发、能收,都验证过了
  • 服务端:能发、能收,也都验证过了
  • 两边的起始序号都对上了

再多一次,还是在重复「我收到了」。TCP 第二次那个包把 SYN 和 ACK 拼在一起发,所以四次能做的事,三次就做完了。

和 UDP 怎么选 ​

TCP 面向连接:先握手,再传。有确认、窗口、重传、拥塞控制。数据要完整、要按顺序,走 TCP。HTTP、文件、邮件都是。代价是更慢、更吃资源,握手也能被人拿去打服务器。

UDP 无连接:不握手、不确认、不保序。快。丢了就丢。音视频、内网小消息、能接受丢失的场景更常见。UDP 头 8 字节,TCP 头至少 20 字节。

记一句:要正确且按顺序,用 TCP;要低延迟且能接受丢,用 UDP。

测试开发工程师 · 专注自动化与系统架构 | 邮箱: hansblog@atumsoul.win