Skip to content

WebSocket

github-actions[bot] edited this page Jul 16, 2026 · 9 revisions

WebSocket 协议指南

wknet::websocket 提供 ws / wss 客户端。wss 默认经 TLS ALPN h2,http/1.1 协商,优先 RFC 8441 extended CONNECT over HTTP/2;失败则 Auto 回落 HTTP/1.1 Upgrade。ws:// 隐式 h2c。

API 签名见 WebSocket API;HTTP/2 隧道原语见 HTTP/2

结论

主题 行为
传输 TransportMode::Auto(默认);Http11Only 强制 H1 Upgrade
wss 默认 offer h2,http/1.1;h2 + ENABLE_CONNECT_PROTOCOL → RFC 8441
分片发送 Send*Ex + FinalFragment=false / SendContinuation
分片接收 默认聚合完整消息;DeliverFragments=true 按 wire 交付
permessage-deflate opt-in;默认关;未请求扩展一律拒绝
Close 主动发 close 后等 peer(3s);被动 echo 后关 transport
Message.Data 指向内部缓冲;下次 Receive / Close 前有效

连接与握手

wknet::websocket::ConnectConfig cfg = wknet::websocket::DefaultConnectConfig();
cfg.Url = "wss://echo.example/ws";
cfg.UrlLength = 21;
// cfg.TransportMode = wknet::http::WebSocketTransportMode::Http11Only;
// cfg.PerMessageDeflate.Enable = true;
wknet::websocket::WebSocket* ws = nullptr;
wknet::websocket::ConnectEx(session, &cfg, &ws);
  • ConnectConfig.Headers 可带 Origin / Authorization / Cookie 等。
  • 库受控头被拒HostConnectionUpgradeContent-LengthTransfer-Encoding、全部 Sec-WebSocket-*
  • Sec-WebSocket-Accept 常量时间比对;子协议须为已请求之一。
  • 默认跟随 opening-handshake 的 3xx / 401 / 407:返回 STATUS_NOT_SUPPORTED 并保留状态码;其它非 101 → STATUS_INVALID_NETWORK_RESPONSE
  • 解析 ≤8 地址逐个尝试;取消令牌贯穿;同步路径 PASSIVE_LEVEL
传输 掩码
HTTP/1.1 Upgrade 客户端帧始终掩码(每帧新随机键)
RFC 8441 over H2 按规范无掩码 DATA 隧道
收到被掩码的服务端帧 协议错误 → close 1002

分片

发送

  • SendText / SendBinary 默认整消息、FinalFragment=true
  • *ExFinalFragment=false 开启分片;后续用 SendContinuation / SendContinuationEx
  • 库按帧缓冲自动切块:首帧真实 opcode,后续 Continuation。
  • 文本消息跨分片增量 UTF-8 校验;最终片不完整码点 → STATUS_INVALID_PARAMETER

接收

DeliverFragments 交付形态
false(默认) 客户端重组完整消息;FinalFragment=true
true 按 wire:首帧 Text/Binary,续帧 Continuation;FinalFragment = 真实 FIN

文本仍跨片 UTF-8 校验;最终片非法 → close 1007 / STATUS_INVALID_NETWORK_RESPONSE

Message.Data 生命周期

Message.Data / DataLength 指向库内缓冲,在同句柄下一次成功 Receive 或 Close 之前可读。需要跨调用保留时请自行拷贝。

permessage-deflate(默认关)

  • 开启:ConnectConfig.PerMessageDeflate.Enable=true
  • 可配 ClientNoContextTakeover / ServerNoContextTakeover / ClientMaxWindowBits / ServerMaxWindowBits(仅 8..15)。
  • H1 Upgrade 与 RFC 8441 共用同一 offer/校验路径;调用方不得手写 Sec-WebSocket-Extensions
  • 发送:仅压缩 Text/Binary 首片设 RSV1;Continuation / 控制帧不设 RSV1。
  • 接收:未协商、控制帧、Continuation 上的 RSV1 → 协议错误关闭。
  • 解压受 MaxMessageBytes、输出容量与膨胀比限制;不协商 permessage-deflate 以外的 WebSocket 扩展。

控制帧与 Close

事件 处理
Ping AutoReplyPing 默认开 → 自动 Pong
单次接收控制帧 >100 close 1008
被掩码帧 / 分片状态错 close 1002
非法 UTF-8(文本/close) close 1007
MaxMessageBytes close 1009STATUS_BUFFER_TOO_SMALL

合法入站 close 码:1000–1014(除 1004/1005/1006)或 3000–4999;长度恰为 1 的 close payload → 协议错误。

Close 时序

  1. 主动 Close / CloseEx:发 close(CloseEx reason ≤123 字节)→ 等待 peer close(默认 3s;超时/终止吞为成功)→ 关 transport。
  2. 被动:收到 peer close → echo → 关 transport。
  3. 并发约束Close 不得与同句柄「新 I/O 发起」并发;最安全为单线程 连接→发→收→关。

边界

  • 无默认压缩;无默认握手 redirect / 401/407 跟随。
  • 不支持 WebSocket over HTTP/3(见 HTTP/3)。
  • 支持范围见 能力边界

English

WebSocket Protocol Guide

wknet::websocket is the ws / wss client. For wss, TLS ALPN defaults to h2,http/1.1 and prefers RFC 8441 extended CONNECT over HTTP/2; Auto falls back to HTTP/1.1 Upgrade when that path is unavailable. ws:// does not implicitly use h2c.

API signatures: WebSocket API. HTTP/2 tunnel primitive: HTTP/2.

Summary

Topic Behavior
Transport TransportMode::Auto (default); Http11Only forces H1 Upgrade
wss Offers h2,http/1.1 by default; h2 + ENABLE_CONNECT_PROTOCOL → RFC 8441
Fragmented send Send*Ex + FinalFragment=false / SendContinuation
Fragmented receive Default: reassemble whole messages; DeliverFragments=true for wire fragments
permessage-deflate Opt-in; off by default; unrequested extensions rejected
Close Active: send close then wait (3s); passive: echo then close transport
Message.Data Points at an internal buffer; valid until the next Receive / Close

Connect and handshake

wknet::websocket::ConnectConfig cfg = wknet::websocket::DefaultConnectConfig();
cfg.Url = "wss://echo.example/ws";
cfg.UrlLength = 21;
// cfg.TransportMode = wknet::http::WebSocketTransportMode::Http11Only;
// cfg.PerMessageDeflate.Enable = true;
wknet::websocket::WebSocket* ws = nullptr;
wknet::websocket::ConnectEx(session, &cfg, &ws);
  • ConnectConfig.Headers may carry Origin / Authorization / Cookie, etc.
  • Library-controlled headers are rejected: Host, Connection, Upgrade, Content-Length, Transfer-Encoding, all Sec-WebSocket-*.
  • Sec-WebSocket-Accept is compared in constant time; the selected subprotocol must be one that was offered.
  • Opening-handshake 3xx / 401 / 407 are not followed by default: STATUS_NOT_SUPPORTED with the status preserved; other non-101 → STATUS_INVALID_NETWORK_RESPONSE.
  • Up to 8 resolved addresses are tried; cancellation tokens apply end-to-end; sync paths require PASSIVE_LEVEL.
Transport Masking
HTTP/1.1 Upgrade Client frames are always masked (fresh key per frame)
RFC 8441 over H2 Unmasked DATA tunnel per the RFC
Masked server frame Protocol error → close 1002

Fragmentation

Send

  • SendText / SendBinary default to whole messages with FinalFragment=true.
  • Set FinalFragment=false on *Ex to start a fragmented message; continue with SendContinuation / SendContinuationEx.
  • The library auto-chunks to the frame buffer: first frame uses the real opcode, later frames use Continuation.
  • Text is validated with incremental cross-fragment UTF-8; an unfinished code point on the final fragment → STATUS_INVALID_PARAMETER.

Receive

DeliverFragments Delivery
false (default) Client-reassembled complete message; FinalFragment=true
true Wire fragments: first Text/Binary, then Continuation; FinalFragment = real FIN

Text still uses cross-fragment UTF-8 validation; a bad final fragment closes with 1007 / STATUS_INVALID_NETWORK_RESPONSE.

Message.Data lifetime

Message.Data / DataLength refer to a library buffer readable until the next successful Receive or Close on the same handle. Copy the bytes if you need them longer.

permessage-deflate (off by default)

  • Enable with ConnectConfig.PerMessageDeflate.Enable=true.
  • Configurable: ClientNoContextTakeover / ServerNoContextTakeover / ClientMaxWindowBits / ServerMaxWindowBits (only 8..15).
  • H1 Upgrade and RFC 8441 share the same offer/validation path; callers must not hand-write Sec-WebSocket-Extensions.
  • Send sets RSV1 only on the first compressed Text/Binary fragment; continuations and control frames never set RSV1.
  • Receive treats RSV1 on unnegotiated links, control frames, or continuations as a protocol error and closes.
  • Inflate is bounded by MaxMessageBytes, output capacity, and expansion ratio. WebSocket extensions other than permessage-deflate are not negotiated.

Control frames and Close

Event Handling
Ping AutoReplyPing defaults on → automatic Pong
>100 control frames per receive close 1008
Masked frame / bad fragment state close 1002
Illegal UTF-8 (text/close) close 1007
Over MaxMessageBytes close 1009 (STATUS_BUFFER_TOO_SMALL)

Valid inbound close codes: 1000–1014 (except 1004/1005/1006) or 3000–4999; a close payload of length exactly 1 is a protocol error.

Close sequencing

  1. Active Close / CloseEx: send close (CloseEx reason ≤123 bytes) → wait for peer close (default 3s; timeout/termination swallowed as success) → close transport.
  2. Passive: receive peer close → echo → close transport.
  3. Concurrency: never start new I/O on the same handle concurrently with Close; safest pattern is single-threaded connect→send→receive→close.

Boundaries

  • No compression by default; no default handshake redirect / 401/407 following.
  • WebSocket over HTTP/3 is not supported (see HTTP/3).
  • See the capability matrix for support scope.

Clone this wiki locally