-
Notifications
You must be signed in to change notification settings - Fork 0
WebSocket
enum class WebSocketOpcode : UCHAR {
Continuation=0x0, Text=0x1, Binary=0x2, Close=0x8, Ping=0x9, Pong=0xA
};常量:client key 16 字节(base64 24)、Accept 28、掩码键 4、帧头最大 14、控制帧 payload ≤125。
WebSocketCodec(静态):
-
GenerateClientKey:16 随机字节(CNG)→ base64,用后清零。 -
ComputeAcceptValue:SHA-1(key + 258EAFA5-...) → base64。 -
ValidateServerHandshake:要求101、HTTP/1.1、Connection: Upgrade、Upgrade: websocket、恰好一个Sec-WebSocket-Accept(常量时间比对);只接受已请求且参数合法的permessage-deflate,未请求扩展、未知扩展、重复/非法参数一律拒绝;子协议须为请求过的之一。 -
EncodeClientFrame:客户端帧必掩码(payload XOR 4 字节键);控制帧必须 FIN 且 ≤125。 -
DecodeFrameHeader:RSV2/RSV3 必须为 0;RSV1 仅供已协商的permessage-deflate首个 Text/Binary 片使用;被掩码的服务端帧 →STATUS_INVALID_NETWORK_RESPONSE;扩展长度强制最短编码。
产品 API(wknet::websocket,见 产品 API)
// 连接(同步/异步,URL 或 ConnectConfig)
wknet::websocket::Connect / ConnectEx / ConnectAsync / ConnectAsyncEx ; wknet::websocket::AsyncGetWebSocket
// 发送(各有 *Ex 带 SendOptions{ bool FinalFragment })
wknet::websocket::SendText / SendBinary / SendContinuation / SendPing / SendPong
// 接收 / 关闭 / 查询
wknet::websocket::Receive / ReceiveEx ; wknet::websocket::Close / CloseEx ; wknet::websocket::SelectedSubprotocolenum class wknet::websocket::MsgType { Text, Binary, Close, Continuation, Ping, Pong };
struct wknet::websocket::Message { MsgType Type; const UCHAR* Data; SIZE_T DataLength; bool Final; bool FinalFragment; };
struct wknet::websocket::ReceiveOptions { SIZE_T MaxMessageBytes; bool AutoAllocate=true; MessageCallback OnMessage; void* CallbackContext; bool DeliverFragments=false; };
struct wknet::websocket::Header { const char* Name; SIZE_T NameLength; const char* Value; SIZE_T ValueLength; };
struct wknet::websocket::ConnectConfig { const char* Url; SIZE_T UrlLength; const char* Subprotocol; SIZE_T SubprotocolLength;
const Header* Headers; SIZE_T HeaderCount;
websocket::PerMessageDeflateOptions PerMessageDeflate;
wknet::http::TlsConfig Tls; wknet::http::AddressFamily Family; SIZE_T MaxMessageBytes; bool AutoReplyPing=true; };-
ConnectConfig.Headers可传调用方自定义握手头,例如Origin、Authorization、Cookie。 - 为防止握手被篡改,库受控头会被拒绝:
Host、Connection、Upgrade、Content-Length、Transfer-Encoding、Sec-WebSocket-Key、Sec-WebSocket-Version、Sec-WebSocket-Protocol、Sec-WebSocket-Extensions。 - 字段名/值走普通 HTTP header 文本校验,拒绝空名、超限长度、控制字符与 CRLF 注入。
- 默认不跟随 opening-handshake 的 redirect、401/407 challenge:3xx/401/407 返回
STATUS_NOT_SUPPORTED并保留握手状态码;其它非 101 响应返回STATUS_INVALID_NETWORK_RESPONSE。跨源 redirect、认证重放等若未来支持,必须通过显式 opt-in 并沿用 HTTP redirect 安全规则。
- 默认关闭:
DefaultConnectConfig()不启用压缩,零初始化配置也不启用压缩;未请求时服务端返回任何扩展都会拒绝。 - 启用方式:设置
ConnectConfig.PerMessageDeflate.Enable=true。可配置ClientNoContextTakeover、ServerNoContextTakeover、ClientMaxWindowBits、ServerMaxWindowBits,window bits 只接受8..15,非法配置返回STATUS_INVALID_PARAMETER。 - 握手:HTTP/1.1 Upgrade 与 RFC 8441 HTTP/2 Extended CONNECT 使用同一 offer 生成逻辑;调用方仍不能在
Headers中手写Sec-WebSocket-Extensions。 - 数据路径:发送 Text/Binary 首片压缩并设置 RSV1,Continuation 不设置 RSV1;接收时只允许首片 Text/Binary 携带 RSV1,control frame、Continuation、未协商压缩时出现 RSV1 均按协议错误关闭。
- 安全边界:解压受
MaxMessageBytes、输出容量与膨胀比限制约束;context takeover 按协商结果保留或在每条消息后重置。
-
发送:
wknet::websocket::SendContinuation(Ex)续帧;SendText/SendBinary的*Ex可带FinalFragment=false开启分片。客户端会按帧缓冲自动分块(首帧用真实 opcode,后续用 Continuation),并对文本消息跨分片增量 UTF-8 校验,最终片不完整码点 →STATUS_INVALID_PARAMETER。 -
接收:默认
ReceiveOptions.DeliverFragments=false,ReceiveOptions.OnMessage回调或默认返回式都返回客户端已重组的完整消息,finalFragment=true。显式设置DeliverFragments=true后,接收路径按 wire fragment 交付:首帧返回 Text/Binary,续帧返回 Continuation,finalFragment承载真实 FIN;文本消息仍跨分片做增量 UTF-8 校验,最终片不完整码点 → close 1007 /STATUS_INVALID_NETWORK_RESPONSE。Message.Data指向内部缓冲,下次收/关前有效。
- 控制帧:
AutoReplyPing(默认开)自动回 Pong;单次接收控制帧 > 100 → close 1008(WsMaxControlFramesPerReceive)。 - 校验失败均以 close 帧失败连接:被掩码帧/分片状态错 → 1002;文本/close payload 非法 UTF-8 → 1007;累计超
MaxMessageBytes→ 1009(STATUS_BUFFER_TOO_SMALL)。 - 合法接收 close 码:1000–1014(除 1004/1005/1006)或 3000–4999;长度恰为 1 的 close payload → 协议错误。
- close 握手:主动
Close发空 close 后WaitForPeerClose(WskCloseTimeoutMilliseconds = 3000超时,吞掉终止/超时为成功);被动收到 peer close 则 echo 后关 transport。CloseExreason ≤123 字节。 - 连接:解析 ≤8 地址逐个尝试;
wss默认 WS 握手 ALPN 为h2,http/1.1,协商到 h2 后走 RFC 8441,peer 未启用SETTINGS_ENABLE_CONNECT_PROTOCOL时按 Auto 规则重试 HTTP/1.1;显式Http11Only只走 HTTP/1.1,ws://不隐式走 h2c;每地址都做 TLS1.2 确认重连;取消令牌贯穿。 -
全双工时序:
Close不得与同句柄「新 I/O 发起」并发;最安全单线程内 连接→发→收→关。
wknet::websocket::ConnectConfig 默认 TransportMode::Auto;wss 可通过 RFC 8441 extended CONNECT over HTTP/2 建立隧道,不支持时回到 HTTP/1.1 Upgrade;Http11Only 可强制 HTTP/1.1。
wknet::websocket::WebSocket* ws = nullptr;
if (NT_SUCCESS(wknet::websocket::Connect(session, "wss://echo.example/ws", 21, &ws))) {
wknet::websocket::SendText(ws, "hello", 5);
wknet::websocket::Message msg = {};
if (NT_SUCCESS(wknet::websocket::Receive(ws, &msg)) && msg.Type == wknet::websocket::MsgType::Text) {
// 使用 msg.Data / msg.DataLength(下次收/关前有效)
}
wknet::websocket::Close(ws);
}Framing lives in the internal wknet::ws layer; the public API is wknet::websocket. Client frames are masked on HTTP/1.1 and unmasked inside RFC 8441 DATA tunnels. Server handshake validation compares Sec-WebSocket-Accept in constant time and rejects unrequested or invalid extensions.
Fragmentation: send is fully granular via wknet::websocket::SendContinuation(Ex) / FinalFragment=false (auto-chunked to the frame buffer, with incremental cross-fragment UTF-8 validation of text). Receive defaults to ReceiveOptions.DeliverFragments=false, so callbacks and the default return form deliver a client-reassembled complete message with finalFragment=true. Set DeliverFragments=true to receive wire fragments: the first frame reports Text/Binary, continuations report Continuation, and finalFragment reflects the real FIN bit. Text receive still uses incremental cross-fragment UTF-8 validation; an unfinished code point on the final fragment closes with 1007 / STATUS_INVALID_NETWORK_RESPONSE. Message.Data is valid until the next receive/close.
ConnectConfig.Headers accepts caller-supplied opening-handshake headers such as Origin, Authorization, and Cookie; controlled handshake headers (Host, Connection, Upgrade, Content-Length, Transfer-Encoding, and Sec-WebSocket-*) plus invalid header text are rejected.
permessage-deflate: supported as an explicit opt-in through ConnectConfig.PerMessageDeflate.Enable=true (and the lower-level connect options). Defaults stay off: DefaultConnectConfig() and zero-initialized configs do not offer compression, and any server extension is rejected when not requested. ClientNoContextTakeover, ServerNoContextTakeover, ClientMaxWindowBits, and ServerMaxWindowBits are configurable; window bits must be 8..15 or connect returns STATUS_INVALID_PARAMETER. HTTP/1.1 Upgrade and RFC 8441 HTTP/2 Extended CONNECT share the same offer/validation path. Callers still cannot hand-write Sec-WebSocket-Extensions. The send path sets RSV1 only on the first compressed Text/Binary fragment; continuations and control frames never set RSV1. Receive rejects RSV1 on control frames, continuations, or unnegotiated connections, and inflate is bounded by MaxMessageBytes, output capacity, and expansion-ratio limits.
Behavior: auto-Pong (toggleable); >100 control frames per receive -> close 1002 lineage (masked/fragment-state errors), 1007 (bad UTF-8), 1008 (control flood), 1009 (over MaxMessageBytes). Valid incoming close codes 1000-1014 (minus 1004/1005/1006) or 3000-4999. Active close sends an empty close then waits for peer close (3 s timeout, swallowed as success); passive close echoes then closes. wss handshake ALPN defaults to h2,http/1.1 and uses RFC 8441 when h2 is negotiated and enabled by the peer; Auto retries HTTP/1.1 when RFC 8441 is unsupported, Http11Only forces HTTP/1.1, and ws:// does not implicitly use h2c. Opening-handshake redirects and 401/407 challenges are not followed by default: 3xx/401/407 return STATUS_NOT_SUPPORTED with the response status visible to the caller, while other non-101 responses return STATUS_INVALID_NETWORK_RESPONSE. Never run Close concurrently with new I/O on the same handle. Boundaries: no compression by default, no handshake redirect/401/407 following.
-
开始使用 / Get Started
-
概念与行为 / Concepts
-
协议指南 / Protocols
-
API 参考 / API Reference
-
贡献与项目 / Contribute