抓包看一个 HTTP 请求,你会发现一个反直觉的事:真正发数据之前,有一堆看不懂的握手包在飞。这些包不是 HTTP 的,是 TCP 的——HTTP 得先把路修好,才能在上面跑车。
这一堆握手包就是 TCP 的三次握手。很多人觉得它是个「背了就忘」的八股,但它是 HTTP 性能优化的真正起点——后面讲 keep-alive、HTTP/2 多路复用、HTTP/3 换 QUIC,全是在跟握手的开销较劲。先把这一篇的基础打实。
三次握手到底在握什么
握手不是走形式,它要解决两个问题:双方确认彼此都能收发,协商初始序号。
跟着上图走一遍序号变化:
- 第一次握手(客户端 → 服务器):客户端发 SYN,带上自己的初始序号
seq=x。这步的意思是「我想连你,我从序号 x 开始发数据」。此时客户端进入 SYN_SENT 状态。 - 第二次握手(服务器 → 客户端):服务器收到 SYN,回 SYN+ACK。
ack=x+1(确认收到你的 x,我期待下一个是 x+1),同时带上自己的初始序号seq=y。这步同时做了两件事:确认收到了客户端的 SYN,并发出自己的 SYN。服务器进入 SYN_RCVD 状态。 - 第三次握手(客户端 → 服务器):客户端发 ACK,
ack=y+1(确认收到你的 y)。这步可以顺便带数据。连接建立,双方进入 ESTABLISHED 状态。
注意第二次握手是 SYN+ACK 合并发的,不是分两步——这是能三次握完的关键。
为什么不是两次
这是最经典的追问。如果只握两次:客户端发 SYN,服务器回 ACK,连接就建好了——行不行?
不行,问题出在网络延迟上。假设客户端发了一个 SYN,但这个包在网络里堵了很久没到。客户端超时了,重发一个新的 SYN(新的序号),这次正常握完、传完数据、关闭连接。
这时候第一个「堵车」的 SYN 终于到了服务器。如果只握两次,服务器收到 SYN 就立刻建好连接,进入 ESTABLISHED 状态等着客户端发数据。但客户端根本不知道有这个连接——它早就关闭了。于是服务器就傻等,白白占着资源等一个永远不会来的客户端。
三次握手能避免这个问题:服务器收到那个迟到的 SYN,回 SYN+ACK,但不会立刻建立连接,要等客户端的第三次 ACK。客户端收到这个莫名其妙的 SYN+ACK(它没发起过这个连接),会回一个 RST(重置)而不是 ACK,服务器就知道「这是个无效的旧请求」,放弃连接。
一句话:第三次握手是为了确认客户端真的还在,防止历史重复的连接请求让服务器空等。
为什么不是四次
如果三次已经够了,四次就是浪费。三次已经完成了双向确认:第一次确认客户端能发,第二次确认服务器能收能发,第三次确认客户端能收。双向收发能力都验证了,再握一次纯粹是重复劳动。
TCP 的设计哲学是「够用就好,不冗余」。三次是验证双向可靠通信的数学最小值,四次没有任何额外收益。
握手的代价:1 个 RTT
握手不是免费的,它的代价是时间。从客户端发 SYN 到收到服务器的 SYN+ACK,要等一个来回——这就是 1 个 RTT(Round-Trip Time,往返时延)。第三次握手的 ACK 发出去不需要等回复(可以和第一个数据包一起发),所以总开销是 1 个 RTT。
把一次完整的 HTTP 请求拆开,耗时构成是这样的:
- TCP 握手:1 RTT
- TLS 握手(如果是 HTTPS):1~2 RTT
- HTTP 请求 + 响应:1 RTT
也就是说,一个 HTTPS 请求在新建连接时,光是建连接就要花 2~3 个 RTT,其中握手占一半。如果 RTT 是 50ms,光握手就 50ms,还不算传输数据。
这就是为什么后面要讲 keep-alive(复用连接省掉重复握手)、HTTP/2 多路复用(一条连接跑多个请求)、HTTP/3 用 QUIC(把握手和加密合并,做到 0-RTT)——所有这些优化,说到底都是在减少握手的 RTT 开销。
取舍与边界
三次握手这套设计有几个边界值得知道:
- SYN Flood 攻击:攻击者疯狂发 SYN 但不回第三次 ACK,服务器一堆半连接(SYN_RCVD 状态)占满资源。防御是 SYN Cookie 技术——服务器不分配资源,把状态编码进 SYN+ACK 的序号里,等第三次 ACK 再验证。
- SYN+ACK 合并是优化:理论上服务器的确认(ACK)和自己的 SYN 可以分开发,但合并成 SYN+ACK 省了一次往返。如果拆开发,就变成四次了。
- 握手可以带数据(TCP Fast Open):RFC 7413 允许第三次握手的 ACK 顺便带数据,省一个 RTT。但需要客户端和服务端都支持,部署不普遍。
收束:握手是 HTTP 性能的地基
TCP 三次握手建立了可靠传输的基础,但它带来的 1 个 RTT 开销,是 HTTP 所有性能优化的出发点。理解了它,你才会明白为什么 keep-alive 有价值(省掉重复握手)、为什么 HTTP/2 要多路复用(一条连接抵过去 N 条)。
下一篇讲 keep-alive——既然每次握手都贵,那能不能握一次手,多跑几个请求?
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 HTTP」,欢迎关注公众号 「十三Tech」。后续会按 URL 与报文、连接与传输、缓存与协商、安全与边界、HTTP/2 与 HTTP/3 这条线更新。

