再探 WebSocket -不止于全双工

如果你像我一样,对 HTTP/1/2/3 的演进和 gRPC 的四种调用模式很了解,但谈起 websocket 时认知止于全双工通信,那么 WebSocket 值得我们用同样的视角,重新审视一番。

参照对比:它与 HTTP 有何不同?和另一个全双工的 gRPC 双向流又有什么区别?

1. 对比 HTTP/1, 2, 3

将 HTTP 简单地描述为“一问一答”不够准确。
从基于 HTTP/1.1 的 SSE (Server-Sent Events) 到 HTTP/2 和 HTTP/3 的原生多路复用流,HTTP 已经发展出强大的流式处理能力。

这些流与 WebSocket 的核心区别在于它们的通信模型:

  • HTTP 的流是“事务性”的:

    • SSE 提供了一个高效的服务器到客户端的单向流,非常适合状态更新和通知推送。但它就像一个广播,客户端无法通过同一连接回话。
    • HTTP/2 和 HTTP/3 的流虽然是双向的,并且可以流式传输数据,但每个流的生命周期和语义都与一次**“请求-响应”**事务绑定。客户端发起请求,服务器给予响应。服务器不能在没有客户端请求的情况下,主动发起一个流来推送消息。
  • WebSocket 的流是“对话式”的:

    • WebSocket 在通过 HTTP 握手建立连接后,就完全脱离了 HTTP 的事务模型。整个连接变成了一条持久、对称、全双工的消息通道。
    • 双方地位完全平等,没有“请求方”和“响应方”之分。任何一方都可以在任何时候主动向对方发送消息。

2. 对比gRPC 双向流

gRPC 的双向流和 WebSocket 都是全双工通信,但它们的抽象层次和设计哲学截然不同。我曾经也使用过 RPC 双向流以及 webtransport 来做协同画布这种业务,和使用websocket还是很不同的

  • gRPC 双向流: 每一步都是按照预先定义好的proto。这是一个有上下文、有强类型约束的结构化对话。gRPC 帮你处理了所有的序列化和规则校验,你只用专注于业务逻辑。
  • WebSocket: 不关心通信的内容和格式,只保证二进制数据能清晰、完整地传到对方。你想在上面实现自己的业务逻辑,需要自己定义消息格式(比如用 JSON 或 Protobuf)并维护状态。它提供的是一个无结构、无上下文的通用消息通道。

WebSocket 的语法、语义与数据格式

  • 语义 (Semantics): 如前所述,是**面向消息(Message-Oriented)**的。连接一旦建立,通信单元就是一个个独立的“消息”,没有请求和响应的内在关联。

  • 语法 (Syntax): WebSocket 的语法单元是**“帧”(Frame)**。这正是它高效的秘密所在。一次网络传输就是一个或多个帧的组合。一个帧由两部分构成:

    • 一个二进制帧头(2-14 字节)
    • 数据载荷(Payload)

    这个二进制帧头里只包含了最基本的信息:这是不是消息的最后一帧(Fin)?是文本(OpCode=1)还是二进制(OpCode=2)?数据有多长?为了安全,从客户端发往服务器的帧,其载荷还会被一个 4 字节的随机密钥进行简单的异或(XOR)“掩码”处理。

  • 数据格式 (Data Format): 这是最关键的一点。WebSocket 帧的帧头 在网络上传输时,就是一个连续的二进制字节流。相比 HTTP/1.1 那冗长的纯文本头部,WebSocket 的协议开销(Protocol Overhead)几乎可以忽略不计。每次通信只需付出几个字节的帧头代价,而不是上百字节的 HTTP 头部。而在数据本体方面,websocket是不感知的,可以传文本,也可以传二进制。

对标 HTTPS 的WSS加密

WSS 的本质是 WebSocket over TLS,与 HTTPS (HTTP over TLS) 的原理完全相同。

当你使用 wss:// 协议头(而非 ws://)时,整个通信流程会增加一个 TLS 安全层:

  1. 在 TCP 连接建立后,应用数据传输之前,客户端和服务器会进行一次完整的 TLS 握手。
  2. 这次握手会验证服务器的证书、协商加密算法,并生成一个临时的对称会话密钥。
  3. 一旦 TLS 握手成功,一条加密通道就建立起来了。
  4. 之后,无论是 WebSocket 的 HTTP 升级握手请求,还是后续的所有 WebSocket 数据帧,都会先经过 TLS 层的加密,然后再在网络上传输。

WebSocket + Protobuf

Q: WebSocket 既然是二进制传输,但它又没有像 gRPC 那样的 IDL,如何处理复杂的结构化数据?

A: WebSocket 协议本身不负责定义载荷的内部结构,它只提供高效的传输通道。

这种需求很符合 Protobuf

  • Protobuf 的角色: 通过 .proto 文件定义数据结构,并能将内存中的对象(Struct)序列化成极其紧凑的二进制字节流。
  • WebSocket 的角色: 接过 Protobuf 打包好的二进制包裹([]byte),把它放进自己的标准快递箱(二进制帧),然后以极低的成本快速送到目的地。

这套组合拳对于在线游戏、金融行情推送、实时协作应用等场景非常适用。


再探 WebSocket -不止于全双工
https://whosefrienda.github.io/2026/01/08/WebSocket/
作者
WhosefriendA
发布于
2026年1月8日
许可协议