← 返回题目列表

WebSocket 协议的工作原理是什么?(握手、数据帧、心跳)

高频 中等 第 16 / 27 题 更新于 2026/07/28
WebSocket数据帧心跳长连接

简化版

WebSocket 的工作分两步:先借 HTTP 握手升级,再走自己的帧协议全双工通信。客户端发一个带 Upgrade: websocket 的 HTTP 请求,服务器同意后回 101 Switching Protocols,这条 TCP 连接就从 HTTP「变身」成 WebSocket。之后双方不再发 HTTP 报文,而是发轻量的数据帧(frame)——帧头只有 2~14 字节,客户端发的帧还必须用掩码(mask)处理。因为连接是持久的,双方要靠心跳(Ping/Pong 帧)探活、靠断线重连保证可用。

详细版

一、握手阶段(复用 HTTP)

客户端 →
  GET /chat HTTP/1.1
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==   ← 随机 16 字节的 base64
  Sec-WebSocket-Version: 13

服务器 →
  HTTP/1.1 101 Switching Protocols
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=   ← 由 Key 算出

服务器把客户端的 Sec-WebSocket-Key 拼上一个固定的魔术字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11,做 SHA-1 再 base64,得到 Sec-WebSocket-Accept 返回。客户端校验它,确认对方真的懂 WebSocket(而不是随便一个 HTTP 服务器)。这一步不是为了加密,只是防止误连。

二、数据帧阶段

握手后传输的最小单位是,帧头关键字段:

  • FIN:是否是消息的最后一帧(一条大消息可拆成多帧)。
  • opcode:帧类型——0x1 文本、0x2 二进制、0x8 关闭、0x9 Ping、0xA Pong。
  • MASK:客户端→服务器的帧必须置 1 并携带 4 字节掩码;服务器→客户端不掩码
  • Payload length:负载长度,用 7 / 7+16 / 7+64 位变长表示。

三、保活

  • 心跳:一方发 Ping 帧,对方必须回 Pong 帧,用来确认连接还活着(尤其防止中间的 NAT/代理把空闲连接掐掉)。
  • 断线重连:网络抖动会断连,客户端需检测到断开后带退避地重连,并重新恢复业务状态。

完整版教学

一、为什么要「借 HTTP 握手」而不是自立门户

WebSocket 完全可以设计成一个全新协议、用全新端口,但它偏偏复用 HTTP 来握手,原因是现实网络到处是防火墙和代理。这些中间设备大多只放行 80/443 端口的 HTTP(S) 流量,一个陌生端口的新协议很可能被拦。

WebSocket 的聪明之处:握手时伪装成一个普通 HTTP 请求(走 80/443),只是多带了 Upgrade: websocket 头。中间设备看到的是「一次正常的 HTTP 请求」,予以放行;等服务器回了 101 Switching Protocols,这条已经建立的 TCP 连接就地「升级」,后续不再是 HTTP。既穿透了网络设施,又不用新开端口。

记住这个状态码:101 Switching Protocols,它是 WebSocket 握手成功的标志,专门表示「协议切换」。

二、Sec-WebSocket-Key / Accept 到底防什么

很多人以为这对字段是加密或安全机制,其实不是。它解决的是「握手确认」问题:

  • 客户端发一个随机的 Sec-WebSocket-Key(16 字节随机数的 base64)。
  • 服务器把它拼上固定魔术串 258EAFA5-...SHA-1 后 base64,作为 Sec-WebSocket-Accept 回来。
  • 客户端用同样算法验证。

作用是证明服务器确实理解 WebSocket 协议,而不是某个缓存代理或旧 HTTP 服务器凑巧回了个 101。因为算法是公开固定的,它挡不住恶意攻击(谁都能算),真正的加密要靠 wss(WebSocket over TLS)

三、为什么客户端发帧必须加掩码(面试高频)

这是 WebSocket 里最容易被追问的细节。规范强制:客户端→服务器的每一帧都要用一个随机 4 字节掩码对负载做异或;反过来服务器→客户端不掩码

原因是防御一种叫**「缓存投毒 / 代理污染」的攻击:WebSocket 借用 80 端口,中间的透明代理**可能会把流量误当成 HTTP 来解析、缓存。如果攻击者能让客户端发送一段「精心构造得像 HTTP 请求」的 WebSocket 负载,代理可能被骗着去缓存或转发恶意内容。

加了随机掩码后,同样的明文每次在网络上呈现的字节都不同,代理无法预测和拼出一个可控的「伪 HTTP 请求」,攻击就被瓦解了。而服务器→客户端方向不经过这种「客户端可控 + 代理误解析」的路径,所以不需要掩码。

易错点:掩码不是为了加密(密钥就在帧里,明文可解),纯粹是为了防代理污染。真正加密仍要靠 wss。

四、一条消息怎么被拆成多帧

WebSocket 支持分片(fragmentation):一条很大的消息可以拆成多个帧发送,靠 FIN 位标记是否结束、靠 opcode 标记类型。

帧1: FIN=0, opcode=0x1(文本)   ← 消息开始,还没结束
帧2: FIN=0, opcode=0x0(延续)   ← 中间片,opcode 用 0 表示「接着上一帧」
帧3: FIN=1, opcode=0x0(延续)   ← FIN=1,消息到此结束

好处是发送方不必等整条消息都准备好就能开始发(流式),接收方按 FIN 拼回完整消息。注意控制帧(Ping/Pong/Close)不能被分片,且可以「插队」到数据帧的分片之间发送。

五、心跳保活:连接为什么会「假死」

WebSocket 是长连接,但长连接最大的敌人是中间设备的空闲超时。NAT 网关、负载均衡、反向代理往往会把「一段时间没有数据」的 TCP 连接悄悄回收,而两端应用并不知道——TCP 层面没有数据交换时,连接「看起来还在」,实际已被中间掐断。这就是「假死连接」。

解决办法是应用层心跳

  • WebSocket 协议内置 Ping(0x9)/ Pong(0xA) 控制帧:一方定期发 Ping,对方必须尽快回 Pong。
  • 如果发了 Ping 迟迟收不到 Pong,就判定连接已死,主动关闭并重连。
  • 心跳还能顺便刷新中间设备的空闲计时器,让连接不被回收。

很多框架(如浏览器原生 WebSocket)不直接暴露 Ping/Pong API,实践中常在业务层自己发一个约定的 “ping” 文本消息来做心跳。

六、断线重连:长连接可用性的最后一环

网络切换(WiFi ↔ 4G)、信号抖动、服务端重启都会导致连接断开。健壮的 WebSocket 客户端必须处理重连:

  • 检测断开:监听 onclose / onerror,或心跳超时。
  • 退避重连:不要断了立刻疯狂重连(会打垮服务器),用指数退避 + 随机抖动(1s、2s、4s… 加随机值),避免大量客户端同时重连造成「惊群」。
  • 状态恢复:重连成功后要重新订阅、补拉断线期间漏掉的消息(通常靠服务端记录 last message id / 序号来补偿)。

七、常见误区与追问

考点正确口径
握手HTTP Upgrade 返回 101 Switching Protocols
传输升级后使用 WebSocket 帧
能力长连接、全双工、服务端主动推送
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ...

HTTP/1.1 101 Switching Protocols

WebSocket 只借 HTTP 开门,进门后说的是 WebSocket 帧协议,不再是普通请求响应。

  • 误区:WebSocket 是 HTTP 轮询。 轮询仍是多次 HTTP 请求,WebSocket 是一次握手后的持久全双工连接。
  • 误区:WebSocket 建连后不需要心跳。 长连接可能被 NAT、代理或网络异常断开,需要 ping/pong 或应用心跳探活。
  • 误区:wss 只是换个端口。 wss 是 WebSocket over TLS,提供加密和证书身份校验。
  • 追问:为什么服务端可以主动推送? 连接升级后双方都可独立发送帧,不再受请求-响应模型限制。
  • 追问:Sec-WebSocket-Key 有什么用? 用于握手校验,确认服务端理解 WebSocket 协议,防止普通 HTTP 误升级。
  • 追问:WebSocket 如何做鉴权? 可在握手阶段用 Cookie、Token、Header 或子协议传递身份,并校验 Origin。

八、加强记忆

WebSocket = HTTP 握手升级(101 Switching Protocols)+ 帧协议全双工通信。握手借 HTTP 穿透防火墙;Sec-WebSocket-Key/Accept 只做握手确认不加密。数据以帧传输,FIN 标记结束、opcode 标记类型(文本/二进制/关闭/Ping/Pong);客户端发帧必须加随机掩码防代理污染,服务器不掩码。长连接靠 Ping/Pong 心跳探活(防中间设备把空闲连接掐成「假死」)、靠指数退避重连 + 状态恢复保证可用。加密走 wss。