HTTP 长连接 vs WebSocket:一个只能请求-响应,一个能双向推送
一句话结论(30s)
HTTP 长连接(Keep-Alive)只是复用同一条 TCP 多跑几次一问一答,本质上仍是半双工、服务端无法主动推送;WebSocket 则通过 HTTP Upgrade 握手 + 101 状态码把这条 TCP 连接升级为全双工帧协议。因为只有全双工才能让服务端在消息到达时零延迟主动推送,这正是聊天、行情、游戏等实时场景的刚需——长轮询的「每 30 秒空轮询」浪费资源,WebSocket 只在有消息时才发、零空转。
核心原理(2min)
- HTTP 长连接:
Connection: keep-alive复用 TCP、省三次握手,但仍是半双工一问一答。 - WebSocket 升级:客户端发
Upgrade: websocket+Sec-WebSocket-Key,服务端回101 Switching Protocols+Sec-WebSocket-Accept,之后 TCP 走 WebSocket 帧协议,双向随时互发。 - 为什么不用长轮询:每 30 秒空轮询空耗资源,WebSocket 按需推送零空转。
- 心跳保活:TCP keepalive 默认 2 小时太慢,WebSocket 用应用层 ping/pong 帧(30 秒一次,连续 3 次无 pong 断开重连)。
底层深入(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 API | HTTP 长连接 | 请求-响应模式足够 |
| 实时聊天 | 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 就判定断线并重连,既保活又能快速探活。