三次握手不是仪式,也不是为了显得专业。就一件事:通话开始前,双方都要亲自确认——「我能把话送到你那儿,你也能把话送到我这儿」。
少一次,有人会瞎等;多一次,没必要。
先把场面想成打电话
你给对面打电话,要确认两件事:
- 对面能听见你
- 你能听见对面
所以实际会这样:
- 你:「喂,听得到吗?」
- 对面:「听得到。你听得到我吗?」
- 你:「听得到,开聊。」
第一次只证明了:你能发,对面能收。 第二次才补上:对面能发,你的耳朵也成。 第三次是对面要的那张回执:它那句「你听得到我吗」确实被你听见了。
两次打完,对面其实还不确定你听见没有。它不敢开始倒数据。
换成 TCP 包,就是这三下
- 客户端发 SYN,带上自己的起始序号
j。意思:我要从j开始跟你说话,你在吗。自己进入SYN_SENT。 - 服务端回 SYN+ACK,确认号
j+1,再带上自己的起始序号k。意思:你的j我收到了,下一个我等j+1;我也要从k开始说。自己进入SYN_RECEIVED。 - 客户端再发 ACK,确认号
k+1。意思:你的k我也收到了。两边进入ESTABLISHED,这才开始传数据。
确认号永远是「对方序号 + 1」,翻译成人话就是:下一包我要收这个号。
序号是各自随便挑的起始编号,后面每个字节都按这个号往下排。握手的另一半工作,就是把两边的号对上。号对不上,后面的数据谁也拼不回来。
一次行不行
不行。你喊了一嗓子就开倒数据,对面可能根本不在,或者没听见。你自己也拿不到对面的起始序号,后面的编号对不上。
两次行不行
不行。两个坑。
坑 1:网上会有「迟到的旧请求」
网络会把包弄丢、弄晚。可能发生这种蠢事:
- 你发出一个 SYN,在路上堵着了。
- 你不耐烦,又发了一个,这次连上了,聊完了,挂了。
- 那个旧 SYN 这才晃到服务端。
- 服务端一看:有人要连我。回一个 SYN+ACK,并以为连接已经建好,开始空等数据。
你这边这笔连接早就结束了,不会再理它。服务端就在那儿傻等。
有第三次就不一样:服务端必须再听到你回一句 ACK,才把连接当真。迟到的那单,你根本不会回第三句,服务端等一会儿就放弃。
坑 2:对面那句「我听到了」可能丢
两次握手时,服务端的 SYN+ACK 在路上丢了:
- 你以为连上了,开始倒数据
- 服务端以为还没连上(它的确认你没收到)
- 你倒过去的数据,服务端不当连接,直接扔掉,继续等确认
- 两边各等各的,卡死
第三次 ACK 就是把这层不确定钉死:服务端听到这句,才知道「对方确实收到了我的序号」。
为什么三次就够,不用四次
三次之后:
- 客户端:能发、能收,都验证过了
- 服务端:能发、能收,也都验证过了
- 两边的起始序号都对上了
再多一次,还是在重复「我收到了」。TCP 第二次那个包把 SYN 和 ACK 拼在一起发,所以四次能做的事,三次就做完了。
和 UDP 怎么选
TCP 面向连接:先握手,再传。有确认、窗口、重传、拥塞控制。数据要完整、要按顺序,走 TCP。HTTP、文件、邮件都是。代价是更慢、更吃资源,握手也能被人拿去打服务器。
UDP 无连接:不握手、不确认、不保序。快。丢了就丢。音视频、内网小消息、能接受丢失的场景更常见。UDP 头 8 字节,TCP 头至少 20 字节。
记一句:要正确且按顺序,用 TCP;要低延迟且能接受丢,用 UDP。