websocket本身不规定消息内容结构,只负责字节流传输;真正决定通信语义的是应用层协议,即通过sec-websocket-protocol协商的子协议(如"json-rpc")与自定义消息格式(如含type、id、payload的json结构)共同定义的规则。

WebSocket 本身不规定消息内容结构,只负责字节流传输。真正决定“怎么说话”的,是应用层协议——也就是你设计的 消息协议格式 和通过 Sec-WebSocket-Protocol 协商的 子协议(Subprotocol)。二者配合,才能让通信既高效又可靠。
子协议名:只是握手时的字符串约定,必须严格匹配
子协议不是运行时解析器,也不是加密或压缩机制,它只是一个握手阶段的“口头约定”:
- 客户端用
new WebSocket(url, ["json-rpc", "myapp-v2"])告诉服务器:“我支持这些协议,按顺序选一个” - 服务端(如 FastAPI)必须写
await websocket.accept(subprotocol="json-rpc"),且这个字符串必须完全出现在客户端数组里 - 大小写、连字符、版本号都算不同协议 ——
"json-rpc"和"JSON-RPC"不兼容 - 协议名只能含小写字母、数字、点(.)和短横线(-),不能有空格、下划线或斜杠
- 协商成功后,
ws.protocol会返回该字符串;为""就说明失败,需检查 Network 面板里的请求/响应头
消息协议格式:子协议生效后实际要遵守的规则
一旦子协议协商完成(比如 ws.protocol === "json-rpc"),所有收发数据就必须按你定义的格式处理。常见高效设计方式包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
统一包结构:每个消息用固定字段包裹,例如
{ "id": "req-123", "type": "auth", "payload": { ... } },方便路由、去重、超时控制 -
类型优先于内容:用
type字段区分业务动作("ping"、"join_room"、"data_update"),避免每次解析完整 payload -
二进制优化场景:若传输大量数值或结构化数据,可搭配
ArrayBuffer+Uint8Array,比 JSON 更省带宽和解析开销(但需子协议名明确标识,如"binary.v1") -
轻量级序列化:对性能敏感场景,可用 MessagePack 或 CBOR 替代 JSON,前提是子协议名体现差异(如
"msgpack-v2"),且客户端/服务端都有对应解码器
客户端如何安全使用子协议与消息格式
别依赖连接建立后再判断协议是否生效 —— 必须在 onopen 后立刻验证:
- 检查
ws.protocol是否符合预期,不符则主动关闭并报错,不进入后续逻辑 - 发送首条消息前,先发一个带
type: "handshake"的探测帧,等待服务端确认,避免“连上了却没说对语言” - 所有
send()调用前做格式校验(如必填字段、type枚举值),防止静默失败 - 监听
onmessage时用try/catch包裹JSON.parse(),错误消息应包含原始event.data方便调试 - 反向代理(如 Nginx)默认过滤
Sec-WebSocket-Protocol头,需显式配置proxy_pass_request_headers on;
服务端配合要点(以 FastAPI 为例)
子协议不是摆设,服务端必须全程配合才能发挥价值:
- 接受连接时指定
subprotocol,且该值必须硬编码或从白名单中选取,不能动态拼接 - 收到消息后,先检查
websocket.client_protocol(FastAPI 中可通过websocket.scope.get("subprotocols")获取),再决定用哪套解析逻辑 - 对不同子协议可启用不同中间件:比如
"json-rpc"走 JSON-RPC 2.0 校验,"binary.v1"直接读取bytes并交由专用处理器 - 拒绝不支持的子协议时,不要静默降级 —— 应直接断连并记录日志,避免客户端误以为协商成功
- 子协议一旦选定,整个连接生命周期内不可更改,重连是唯一补救方式
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










