# WebSocket
`http/ws-*` 提供 RFC 6455 帧式双向流——客户端和服务器两端皆可——
基于明文 `ws://`(客户端侧可通过 mbedTLS 使用 `wss://`)。该 API
与 `net/*` 对应,并使用相同的按 fd 的 epoll 回调。
## `http/ws-*` 标准库函数
| 函数 | 返回值 | 用途 |
|---|---|---|
| `[http/ws-server $port %{ :on-connect $fn }]` | ws_server 资源 | 启动 WebSocket 服务器 |
| `[http/ws-connect $url %{ :on-message $fn :on-close $fn :headers $map }]` | ws_conn 的 future | 连接到服务器 |
| `[http/ws-send $conn $msg]` | nil | 发送文本帧 |
| `[http/ws-send-binary $conn $bytes]` | nil | 发送二进制帧 |
| `[http/ws-ping $conn]` | nil | 发送 ping 帧 |
| `[http/ws-close $conn ?code ?reason]` | nil | 关闭连接 |
| `[http/ws-on-message $conn $fn]` | nil | 注册 on-message 回调 |
| `[http/ws-on-close $conn $fn]` | nil | 注册 on-close 回调 |
| `[http/ws-stop $server]` | nil | 停止服务器 |
## WebSocket 服务器
def on-connect {|conn| @doc "A new client connected. Register handlers on the connection." echo "ws client connected" http/ws-on-message $conn { |msg| echo "received: $msg" http/ws-send $conn "echo: $msg" } http/ws-on-close $conn { echo "ws client disconnected" } } def srv [http/ws-server 8080 %{ :on-connect $on-connect }] echo "ws server on :8080" sleep 60000
## WebSocket 客户端
`http/ws-connect` 返回一个 future,握手完成后解析为
`ws_conn` 资源。在选项映射中传入回调即可从一开始就处理消息,
或者稍后用 `http/ws-on-message` 附加。
def on-message {|msg| echo "got: $msg" } def on-close {|code reason| echo "closed: $code $reason" } def conn-fut [http/ws-connect "ws://echo.example.com" %{ :on-message $on-message :on-close $on-close }] def conn [await $conn-fut] http/ws-send $conn "hello" # For TLS: # def conn-fut [http/ws-connect "wss://api.example.com" %{ :on-message $on-message }]
## 帧
三种帧类型:文本、二进制和 ping。用对应的标准库函数发送:
http/ws-send $conn "plain text frame" http/ws-send-binary $conn $byte-list http/ws-ping $conn # server should auto-pong
`on-message` 回调对每个文本或二进制帧触发一次。二进制帧以字节值
列表的形式到达;文本帧以字符串形式到达。
## 关闭
http/ws-close $conn # default code, no reason http/ws-close $conn 1000 "bye" # explicit code and reason
标准关闭码:`1000`(正常)、`1001`(离开)、`1008`(策略违规)、
`1011`(服务器错误)。远端的 `on-close` 处理器会收到
`(code, reason)`。
## 服务器生命周期
def srv [http/ws-server 8080 %{ :on-connect $on-connect }] # ... run for a while ... http/ws-stop $srv # stop accepting, drop connections
## 架构说明
- 每个 `WsConn` 在 `std.heap.c_allocator` 上堆分配,因此在启动它
的 VM 结束后仍然存活(与 HTTP 服务器的设计一致)。
- 按 fd 的 epoll 回调驱动状态机:握手阶段 → 打开 → 帧解析器。
- 帧是增量解析的;一次 `posix.read` 可能产生多个帧,而一个帧可能
跨越多次读取。
- `wss://` 客户端连接通过与 `http_async.zig` 相同的 BIO 回调模型
复用 mbedTLS。当前不支持服务器端 TLS。
- 回调在 epoll 回调内部同步触发,与 `net/on-data` 类似。
## 帧大小限制
| 限制 | 默认值 |
|---|---|
| 读取缓冲区 | 16 KB |
| 最大帧大小 | 1 MB |
| 最大握手大小 | 64 KB |
大于 1 MB 的帧会被拒绝,并关闭连接。
## 示例
### 广播服务器
def clients ^{} def on-connect {|conn| @doc "Track clients in a set so we can broadcast to all." $clients << $conn http/ws-on-message $conn { |msg| # Broadcast to every connected client for c in $clients { if ($c != $conn) { http/ws-send $c $msg } } } http/ws-on-close $conn { # Manual removal — sets don't support removal, so re-add all-but-this set clients [into ^{} [filter [collect $clients] { $it != $conn }]] } } def srv [http/ws-server 8080 %{ :on-connect $on-connect }] sleep 600000
### JSON RPC 客户端
def on-message {|msg| try { set data [json/parse $msg] echo "result: $data~result" } catch _e { echo "bad json: $msg" } } def c [await [http/ws-connect "wss://rpc.example.com" %{ :on-message $on-message }]] http/ws-send $c [json/stringify %{ :jsonrpc "2.0" :id 1 :method "ping" }]
## 快速参考
| 形式 | 用途 |
|---|---|
| `[http/ws-server $port %{ :on-connect $fn }]` | 启动服务器 |
| `[http/ws-connect $url %{ :on-message $fn ... }]` | 连接(返回 future) |
| `[http/ws-send $conn $msg]` | 文本帧 |
| `[http/ws-send-binary $conn $bytes]` | 二进制帧 |
| `[http/ws-ping $conn]` | Ping |
| `[http/ws-close $conn ?code ?reason]` | 关闭 |
| `[http/ws-on-message $conn $fn]` | 设置消息处理器 |
| `[http/ws-on-close $conn $fn]` | 设置关闭处理器 |
| `[http/ws-stop $server]` | 停止服务器 |