WebSocket 协议详解:从 HTTP 握手到全双工通信

  1. 什么是 WebSocket?

WebSocket 是一种基于 TCP 的网络通信协议,定义于 RFC 6455。它允许客户端与服务器建立持久连接,并在同一条连接上进行双向、全双工通信。

传统的 HTTP 通信通常由客户端发起请求,服务器返回响应。如果客户端希望实时获取服务器端的新数据,往往需要重复发送请求,或者采用长轮询等技术。

WebSocket 则不同。连接建立后,客户端和服务器都可以主动发送数据,不必每次通信都重新发起 HTTP 请求。

例如,在即时聊天应用中:

  • 用户 A 发送一条消息,服务器可以立即将消息推送给用户 B。

  • 服务器可以主动通知客户端连接状态变化。

  • 客户端可以在同一条连接上持续发送实时数据。

WebSocket 常见的应用场景包括即时通信、在线游戏、实时监控、交易行情、协同编辑,以及基于 Web 的实时语音和视频信令。

2. WebSocket 与 HTTP 有什么区别?

WebSocket 并不是 HTTP 的替代品。两者解决的问题有所不同。

对比项 HTTP WebSocket
通信模式 通常采用请求-响应模式 全双工、双向通信
连接 HTTP/1.1 可复用连接;HTTP/2 也支持多路复用 建立后保持持久连接
数据发送 通常由客户端请求触发 双方都可以主动发送
通信开销 每次请求有相应的协议开销 建连后使用较轻量的数据帧
典型场景 REST API、网页资源、普通业务请求 实时消息、事件推送、双向流式交互

需要注意,HTTP/1.1 本身也支持持久连接,因此不能简单地认为 HTTP 每次请求都需要重新建立 TCP 连接。WebSocket 的核心优势在于建立连接后,通信不再局限于传统的 HTTP 请求-响应模式。

另外,WebSocket 不会自动提供业务消息的持久化、可靠重放、用户身份授权或消息顺序保证。此类能力通常需要由应用层设计。

3. WebSocket 如何建立连接?

WebSocket 通常通过 HTTP Upgrade 机制发起握手。客户端先发送一个特殊的 HTTP/1.1 请求,要求服务器将连接协议升级为 WebSocket。

例如,客户端可能发送如下请求:

复制代码
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://example.com

其中几个关键字段的含义如下:

  • Upgrade: websocket:请求切换到 WebSocket 协议。

  • Connection: Upgrade:表明本次请求希望升级连接。

  • Sec-WebSocket-Key:客户端生成的随机 Base64 编码值,用于协议握手验证。

  • Sec-WebSocket-Version: 13:指定使用的 WebSocket 协议版本。

  • Origin:浏览器客户端可携带该字段,服务器可据此执行来源校验。

如果服务器接受升级请求,会返回类似下面的响应:

复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Accept 并不是任意生成的字符串。服务器将客户端提供的 Sec-WebSocket-Key 与固定的 GUID 拼接,计算 SHA-1,再进行 Base64 编码,得到该字段的值。

计算过程为:

复制代码
Sec-WebSocket-Accept =
    Base64(
        SHA1(
            Sec-WebSocket-Key +
            "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
        )
    )

这个过程主要用于确认双方使用了正确的 WebSocket 握手机制,并不是身份认证,也不能代替 TLS 加密。

当客户端收到有效的 101 Switching Protocols 响应并完成握手校验后,双方即可在连接上按照 WebSocket 协议交换数据。

握手完成后发生了什么?

在经典的 HTTP/1.1 WebSocket 连接中,握手成功后,连接不再继续承载普通 HTTP 请求和响应,而是交换 WebSocket 数据帧。

需要特别注意:WebSocket over HTTP/2 使用不同的建立方式,通过 HTTP/2 的扩展 CONNECT 机制建立隧道,而不是在 HTTP/2 连接上直接使用 HTTP/1.1 的 101 Switching Protocols 握手。

4. WebSocket 数据帧

WebSocket 的数据并不是简单地将文本或二进制数据直接写入 TCP 连接,而是封装成 WebSocket 帧。

一个基本的数据帧包含以下字段:

  • FIN:表示当前帧是否为一条消息的最后一帧。

  • Opcode:标识帧的类型,例如文本、二进制数据或控制帧。

  • MASK:表示负载数据是否经过掩码处理。

  • Payload Length:负载长度。

  • Masking-Key:当帧被掩码时使用的密钥。

  • Payload Data:实际的数据内容。

常见的 Opcode 包括:

Opcode 含义
0x0 延续帧
0x1 文本帧
0x2 二进制帧
0x8 关闭连接
0x9 Ping
0xA Pong

文本消息使用 UTF-8 编码。二进制消息则适用于音频片段、图片、压缩数据等内容。

一条 WebSocket 消息不一定对应一个帧。当消息较大时,可以拆分成多个帧发送,接收方根据帧标志和类型重新组装消息。

为什么客户端发送的数据需要 Mask?

按照 RFC 6455,客户端发送给服务器的 WebSocket 帧必须经过掩码处理,而服务器发送给客户端的帧不使用这种掩码。

掩码通过一个 32 位密钥与负载字节进行循环异或计算:

复制代码
masked_payload[i] =
    original_payload[i] XOR masking_key[i % 4]

这种机制的主要目的,是降低特定情况下中间网络设备将 WebSocket 流量误识别为其他协议的风险。

掩码不是加密。 它不能保护数据机密性,也不能代替 TLS。需要加密传输时,应使用 wss://。

5. WebSocket 的 Ping、Pong 与心跳机制

长连接面临一个实际问题:连接看起来仍然存在,但对端可能已经不可达。

例如:

  • 网络发生短暂中断。

  • NAT 或防火墙清理了空闲连接。

  • 服务器进程异常或连接状态失效。

  • 移动设备进入休眠状态。

WebSocket 定义了 Ping 和 Pong 控制帧,可用于检测连接状态。

典型流程如下:

复制代码
Client                         Server
  |                               |
  | -------- WebSocket Ping ----> |
  |                               |
  | <------- WebSocket Pong ----- |
  |                               |
  |          Connection OK        |

如果发送 Ping 后,在设定的超时时间内没有收到对应的 Pong,应用可以将连接判定为疑似失效,并执行关闭或重连策略。

需要区分两个概念:

  1. 协议级 Ping/Pong:WebSocket 定义的控制帧。

  2. 应用层心跳 :例如发送 {"type":"heartbeat"} 这样的 JSON 消息。

两者并不相同。应用层心跳需要双方应用程序理解相应的消息格式;协议级 Ping/Pong 则由 WebSocket 协议栈处理。

实际部署时,还应结合负载均衡器、反向代理和防火墙的空闲超时配置设计心跳周期。心跳间隔过短会增加开销,过长则可能导致失效连接被发现得太晚。

6. WebSocket 连接如何关闭?

WebSocket 提供专门的 Close 控制帧,用于关闭连接。

典型的正常关闭流程如下:

复制代码
Client                         Server
  |                               |
  | -------- Close Frame -------> |
  |                               |
  | <------- Close Frame -------- |
  |                               |
  |         TCP Connection        |
  |            Closed             |

Close 帧可以携带关闭状态码和可选的原因描述。双方完成 WebSocket 关闭握手后,再关闭底层 TCP 连接。

一些常见状态码包括:

  • 1000:正常关闭。

  • 1001:端点离开,例如服务器关闭或客户端导航离开。

  • 1002:协议错误。

  • 1003:收到无法接受的数据类型。

  • 1006:表示异常关闭时使用的本地报告状态,不能作为实际发送的 Close 帧状态码。

如果 TCP 连接直接断开,或者网络突然中断,双方可能无法完成正常的 WebSocket 关闭握手。因此,应用程序不能仅依赖 Close 帧判断连接是否健康,还需要处理底层连接错误和超时。

7. WebSocket 与 TCP 的关系

从协议分层来看,WebSocket 通常运行在 TCP 之上:

复制代码
+----------------------------+
|       Application          |
|   Chat / Signaling / API   |
+----------------------------+
|         WebSocket          |
|  Message / Frame / Control |
+----------------------------+
|            TCP             |
|   Ordered Reliable Stream  |
+----------------------------+
|             IP             |
+----------------------------+

TCP 提供可靠、有序的字节流传输;WebSocket 在其上定义消息边界、控制帧和双向通信语义。

这意味着 WebSocket 本身并不直接提供独立于 TCP 的消息传输通道。接收方必须根据 WebSocket 帧头解析消息边界,而不能将一次 TCP read() 返回的数据直接当成一条完整的 WebSocket 消息。

例如,发送方写入一条完整的 WebSocket 消息后,接收方可能:

  • 一次读取就拿到完整帧;

  • 多次读取才拿到完整帧;

  • 一次读取中包含多个帧的全部或部分数据。

这是 TCP 字节流的正常行为,不代表 WebSocket 数据损坏。

对于需要实时传输的应用,TCP 还有一个重要特性:丢包时,后续数据可能因为重传而等待,即队头阻塞。WebSocket 不能消除这一特性,因为它通常运行在 TCP 之上。

8. WebSocket 的安全性

WebSocket 建立的是持久通信通道,因此安全设计不能只关注最初的握手请求。

使用 WSS

生产环境通常应使用 wss://,即运行在 TLS 保护下的 WebSocket 连接。

  • ws://:不提供传输层加密。

  • wss://:使用 TLS 保护传输数据。

WSS 可以保护传输过程中的数据机密性和完整性,但不能自动判断客户端是否有权访问某个业务资源。

进行身份认证和授权

握手成功并不代表客户端已经通过业务身份认证。服务端仍然需要验证用户身份,并针对每条连接及其操作执行必要的授权检查。

浏览器的 WebSocket API 对自定义握手请求头有限制,因此实际项目中常见的认证方式包括安全的 Cookie、受控的票据机制,或通过应用层消息完成认证。具体方案应结合浏览器、跨域要求和服务端架构选择。

校验 Origin

浏览器会在适用情况下发送 Origin 请求头。服务器可以根据允许的来源列表进行校验,以降低跨站 WebSocket 劫持(Cross-Site WebSocket Hijacking,CSWSH)风险。

但 Origin 并不是身份凭证,非浏览器客户端也可能自行构造该字段。

此外,服务端还应考虑消息大小限制、连接数限制、速率限制、输入校验,以及敏感数据日志脱敏等问题。

9. WebSocket 的工程实践

在实际系统中,建立 WebSocket 连接只是第一步。可靠的实时通信系统还需要处理连接生命周期和业务状态。

9.1 断线重连

网络连接可能随时断开,客户端通常需要实现重连机制。

建议采用指数退避,并增加随机抖动,避免大量客户端在服务恢复时同时重连。

例如:

复制代码
retry_delay =
    min(max_delay, base_delay * 2^retry_count)

实际实现时,可以在计算出的延迟上叠加随机抖动,并限制最大重试次数或设置整体重试策略。

9.2 消息丢失与重复

WebSocket 在一条正常工作的 TCP 连接内提供可靠、有序的字节传输,但这不意味着应用层业务消息一定不会丢失或重复。

例如,客户端发送了一条业务消息,服务器已经完成处理,但响应在连接断开前没有到达客户端。客户端重连后重发消息,就可能造成重复处理。

对于有业务价值的消息,可以考虑:

  • 消息 ID;

  • 幂等处理;

  • ACK 确认机制;

  • 断线后的消息补拉;

  • 服务端消息持久化。

是否需要这些机制,取决于业务对可靠性的要求。

9.3 服务端连接管理

对于高并发 WebSocket 服务,服务端通常需要维护连接与用户、会话或业务实体之间的映射关系。

需要特别关注:

  • 每条连接的内存开销;

  • 心跳检测和超时清理;

  • 慢客户端造成的发送队列堆积;

  • 负载均衡和连接迁移;

  • 多实例之间的消息路由;

  • 服务升级时的连接排空与重建。

如果连接数较大,应避免无界缓存待发送消息,并为发送队列设置容量上限和过载处理策略。

10. WebSocket 适合所有实时通信场景吗?

并不是。

WebSocket 适合需要在客户端和服务器之间进行双向实时交互的场景,但不同的通信需求可能需要不同的协议。

需求 可考虑的方案
普通业务请求 HTTP / REST
服务端向浏览器持续推送事件 SSE
浏览器与服务器双向实时通信 WebSocket
浏览器实时音视频媒体传输 WebRTC
消息代理与设备通信 MQTT
基于 HTTP 的远程过程调用 gRPC

这些技术并非完全互斥。例如,实时音视频应用可以使用 WebSocket 交换信令,而使用 WebRTC 传输媒体。

选择协议时,应综合考虑通信方向、消息规模、延迟要求、可靠性、网络环境和基础设施支持情况,而不是仅仅因为应用需要"实时"就直接选择 WebSocket。

总结

WebSocket 通过握手建立持久连接,并在 TCP 字节流之上定义数据帧、消息边界和控制机制,使客户端与服务器能够进行双向、全双工通信。

理解 WebSocket 时,最重要的是把握几个核心概念:

  1. 握手负责建立协议连接,不负责完成业务身份认证。

  2. 数据帧负责承载消息,但一次 TCP 读取不一定对应一条完整消息。

  3. Ping/Pong 可以帮助检测连接状态,但不能保证业务消息不丢失。

  4. TCP 提供可靠、有序的字节流,WebSocket 在其上提供消息和控制语义。

  5. 生产环境还需要设计安全认证、断线重连、消息幂等、连接管理和过载保护。

掌握这些机制,才能从理解协议本身进一步走向可靠的 WebSocket 服务设计与故障排查。

相关推荐
中屹指纹浏览器svip2 小时前
静态 IP和动态 IP,搭配指纹浏览器该怎么选?
网络·网络协议·tcp/ip·中屹指纹浏览器
木子n12 小时前
车载以太网与SOME/IP服务化通信实战-02.状态机与订阅:SOME/IP-SD的三态迁移、重传账与上电风暴
嵌入式硬件·网络协议·车载系统·车载以太网·some/ip
ITxiaobing20232 小时前
选型不踩坑:IPinfo 之外,IP 地址定位服务还有哪些可选项
网络·网络协议·tcp/ip
用户2788607285673 小时前
战争雷霆换区记录:字段里的「未知」不能写成正常
网络协议·游戏开发
日常筹谋记4 小时前
仓储监测无线化改造经验:明治无线组网方案与WL系列Master总站落地复盘
网络·网络协议
溜达的大象5 小时前
管家婆远程访问实战:SQL Server 2022配置、PC分机端修改与固定TCP连接
网络·网络协议·tcp/ip
白帽攻防录8 小时前
SRC 挖洞:QUIC 协议栈双 RCE,HTTP/3 的新攻击面怎么挖
网络·网络协议·http·网络安全·http/3
隐擎fox8 小时前
Linux eBPF 与 XDP 高性能网络出口架构:动态连接追踪、丢包诊断与住宅网关 QoS 调优实战
linux·网络·网络协议·tcp/ip·架构
Multipath7129 小时前
多链路聚合设备:助力智慧农业发展的网络基石——让农田网络不断连,让智能设备不失控
网络·网络协议·5g·udp·无人机