logo
0
0
WeChat Login
docs: 添加示例项目与贡献指南文档

websocket transport

websocket 子包将 message-oriented WebSocket 连接适配为 yamux 所需的可靠有序字节流连接,保持 core yamux 包本身不引入任何第三方 WebSocket 依赖。

目录

设计目标

  • 零强依赖:子包只声明最小接口,不绑定具体 WebSocket 库
  • 安全默认值:默认仅接受 binary frame,防止 text/binary 混用破坏协议边界
  • 生产可控:transport Ping 为显式 opt-in,地址 / deadline 能力按底层实现自动透传
  • 第三方友好:可直接与 gorilla/websocket 等具备 NextReader / NextWriter 风格接口的库配合

架构概览

┌─────────────────────────────────────────────────────────────┐ │ yamux.Session (core) │ │ │ │ │ net.Conn 接口 │ │ │ │ │ ┌──────────┴──────────┐ │ │ │ websocket.Conn │ ← 本适配器 │ │ │ (net.Conn) │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌──────────┴──────────┐ │ │ │ FramedConn │ ← 底层 WebSocket │ │ │ NextReader/Writer │ │ │ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────┘

适配器将每个 binary/text message 视为连续字节流片段:

  • Read:在多个 message 边界间连续读取,保持顺序
  • Write:将一次写调用编码为单个 binary frame
  • Close:关闭底层 WebSocket 连接,并中断阻塞中的 I/O

安装

go get cnb.cool/zishuo/yamux/websocket

快速开始

基础用法

import ( "cnb.cool/zishuo/yamux" "cnb.cool/zishuo/yamux/websocket" ) // 1. 通过适配器创建 net.Conn wsConn, err := websocket.NewConn(rawWSConn, nil) if err != nil { return err } // 2. 创建 yamux session(与 TCP transport 完全一致) session, err := yamux.Client(wsConn, nil) if err != nil { return err } defer session.Close()

便捷构造函数

也可以直接使用子包提供的辅助函数,一步到位创建 session:

// 创建 Client session session, err := websocket.Client(rawWSConn, nil, nil) if err != nil { return err } defer session.Close() // 创建 Server session session, err := websocket.Server(rawWSConn, nil, nil) if err != nil { return err } defer session.Close()

启用 Transport 级 Ping

当连接需要穿越对 idle 敏感的代理或负载均衡器时,可启用 transport 级 Ping:

cfg := &websocket.Config{ PingInterval: 30 * time.Second, PingTimeout: 10 * time.Second, } wsConn, err := websocket.NewConn(rawWSConn, cfg) if err != nil { return err }

底层连接契约

底层 WebSocket 连接需要满足最小接口 FramedConn

type FramedConn interface { NextReader() (messageType int, r io.Reader, err error) NextWriter(messageType int) (io.WriteCloser, error) Close() error }

若底层连接还实现了以下可选接口,适配器会自动增强对应能力:

可选接口能力增强
WriteControl(int, []byte, time.Time) error支持 transport 级 Ping/Pong
SetReadDeadline(time.Time) error支持读 deadline 透传
SetWriteDeadline(time.Time) error支持写 deadline 透传
LocalAddr() net.Addr / RemoteAddr() net.Addr直接暴露地址
UnderlyingConn() net.Conn透透底层 net.Conn 的地址信息

配置

type Config struct { // AcceptTextMessages 允许把 text frame 也当作有序字节流的一部分读取。 // 默认 false;用于承载 yamux 协议负载时通常应保持 false。 AcceptTextMessages bool // PingInterval 控制 transport 级 Ping 频率。 // 默认 0(关闭);当连接需要穿越对 idle 敏感的 proxy / LB 时可启用。 PingInterval time.Duration // PingTimeout 控制单次 transport Ping 的写超时。 // 仅在 PingInterval > 0 时生效;默认 10s。 PingTimeout time.Duration }

默认值:

  • AcceptTextMessages = false
  • PingInterval = 0
  • PingTimeout = 10s

错误处理

var ( // 传入的连接为 nil 或不可用 ErrInvalidConnection = fmt.Errorf("invalid nil websocket connection") // 启用了 Ping,但底层连接不支持 WriteControl ErrPingUnsupported = fmt.Errorf("websocket ping requires WriteControl support") ) // 收到不符合配置约束的 message type type ErrUnexpectedMessageType struct { MessageType int }

生产建议:

  • ErrUnexpectedMessageType 通常意味着对端发送了错误的 frame 类型,建议记录后关闭连接
  • ErrPingUnsupported 在配置校验阶段即返回,可在启动时断言

生产注意事项

  1. 协议负载应使用 binary frame:yamux 的 wire format 包含二进制数据,text frame 可能导致 UTF-8 校验失败
  2. 安全策略在 HTTP upgrade 层配置:WebSocket 的 compression、read limit、origin、auth 应在底层库或 HTTP server 层完成
  3. 按需启用 transport Ping:若链路穿越对空闲连接敏感的 LB / proxy,再启用 transport Ping;纯内网直连场景建议关闭,避免与 yamux 内置 keepalive 重复
  4. 保留 yamux 级治理:调用方仍应通过 yamux.Config 配置 keepalive、timeout、flow-control,transport adapter 仅解决字节流适配问题
  5. Deadline 透传限制:若底层连接不支持 SetReadDeadline / SetWriteDeadline,adapter 只能在操作开始前检查 deadline 是否已过期,无法强制打断底层阻塞 I/O