Skip to content
Go back

HTTP长连接 vs WebSocket——从半双工到全双工的演进

HTTP 长连接 vs WebSocket:一个只能请求-响应,一个能双向推送

一句话结论(30s)

HTTP 长连接(Keep-Alive)只是复用同一条 TCP 多跑几次一问一答,本质上仍是半双工、服务端无法主动推送;WebSocket 则通过 HTTP Upgrade 握手 + 101 状态码把这条 TCP 连接升级为全双工帧协议。因为只有全双工才能让服务端在消息到达时零延迟主动推送,这正是聊天、行情、游戏等实时场景的刚需——长轮询的「每 30 秒空轮询」浪费资源,WebSocket 只在有消息时才发、零空转。

核心原理(2min)

底层深入(5-10min)

HTTP 长连接(Keep-Alive)

HTTP/1.1 默认 Connection: keep-alive。一条 TCP 连接上可以发送多个 HTTP 请求——复用连接、减少三次握手。

但本质仍是一问一答(半双工):必须客户端发起请求,服务端才能响应。服务端不能主动向客户端推送数据。

思考:为什么长连接省了握手,却还是「半双工」?因为 keep-alive 只是「复用了同一条 TCP」,没改变「必须客户端先请求、服务端才能响应」的 HTTP 语义。服务端哪怕有消息要推,也只能干等客户端来问——连接是持久的,但话语权始终在客户端手里。

WebSocket:全双工

WebSocket 通过 HTTP Upgrade 握手后,将 HTTP 连接升级为 WebSocket 协议:

客户端:
  GET /chat HTTP/1.1
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Key: dGhlIHN...   ← 随机串

服务端:
  HTTP/1.1 101 Switching Protocols
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Accept: s3pPL...   ← 基于客户端的 Key + 固定 GUID 计算哈希

101 状态码之后,TCP 连接不再走 HTTP 协议,而是走 WebSocket 帧协议。客户端和服务端可以随时互发数据(全双工),不需要先等对方请求。

思考:凭什么一个 Upgrade 头就能把 HTTP 变成双向通道?因为 101 状态码之后,这条 TCP 就「换了协议」——不再按 HTTP 的请求-响应报文解析,而是按 WebSocket 的帧协议收发。帧里可以随时双向互发,不再要求「一问一答」。Sec-WebSocket-Key/Accept 只是借 HTTP 之手完成一次「协议切换的握手」。

为什么不用 HTTP 长轮询(Long Polling)?

客户端: 有消息吗?
服务端: 没有……(hold 30秒不回复)
客户端: 有消息吗?(30秒超时后重发)
服务端: 有了!给

长轮询让服务端的”推送”变成了”延迟响应”,但每 30 秒一次空轮询消耗资源。WebSocket 只在有消息时才发——零空转。

思考:长轮询已经能「变相推送」了,为什么还要 WebSocket?因为长轮询是「用延迟换推送」——服务端把响应 hold 住 30 秒等消息,超时后客户端立刻重发,产生大量空轮询请求和空耗连接。WebSocket 建好后,有消息才发帧、没消息就安静,把「轮询的空转开销」降到了零。

适用场景

场景推荐理由
REST APIHTTP 长连接请求-响应模式足够
实时聊天WebSocket服务端需要主动推送
股票行情WebSocket高频推送;长轮询太浪费
网页游戏WebSocket双向实时通信
静态资源HTTP/2多路复用 + 服务器推送

WebSocket 的心跳

TCP 层面有 keepalive 但检测很慢(默认 2 小时)。WebSocket 应用层实现 ping/pong 心跳:

客户端: WebSocket Ping frame
服务端: WebSocket Pong frame
每 30 秒一次,连续 3 次无 pong → 断开重连

总结

HTTP 长连接是”一个 TCP 连接多跑几次一问一答”,WebSocket 是”把 TCP 连接升级成全双工通道”。Upgrade 头 + 101 状态码就是这条升级通道的”签证”。

章末提问

Q1:HTTP 长连接和 WebSocket 的本质区别是什么?

结论先行:长连接只是复用 TCP 的一问一答,仍是半双工;WebSocket 把 TCP 升级成全双工帧协议,可双向随时推送。因为 keep-alive 不改变「客户端必须先请求」的 HTTP 语义,服务端无法主动推;WebSocket 升级后双方对等,谁有消息谁发。

Q2:WebSocket 是怎么从 HTTP 升级的?

结论先行:客户端发 Upgrade: websocket + Sec-WebSocket-Key,服务端回 101 Switching Protocols + Sec-WebSocket-Accept,之后 TCP 改走帧协议。因为 101 就是「协议切换」的签证,Sec-WebSocket-Accept 由客户端的 Key 加固定 GUID 哈希算出,用于证明服务端理解并同意升级,此后连接彻底脱离 HTTP 语义。

Q3:WebSocket 为什么需要心跳?怎么实现?

结论先行:因为 TCP keepalive 检测太慢(默认 2 小时),无法及时发现对端掉线;WebSocket 用应用层 ping/pong 帧保活。因为网络中间设备(NAT/代理)会静默断开空闲连接,应用层每 30 秒发 ping、连续 3 次无 pong 就判定断线并重连,既保活又能快速探活。


Share this post on:

Previous Post
Nginx负载均衡——5种算法与生产实践
Next Post
HTTP状态码与方法语义——从GET/POST到502/504