必须设 socket.binarytype = 'arraybuffer',否则浏览器默认传 blob 导致 decode 失败;禁用 int64/uint64 防 js 精度丢失;超 50kb 消息须分片并加序号头;高频 repeated 数值字段必加 [packed=true]。

WebSocket 传 Protobuf 本身不难,难的是传得稳、解得快、不出错——尤其在移动端弱网下,decode() 报 "truncated" 或 "invalid wire type" 不是序列化错了,而是通道没设对、包没切好、字段类型选歪了。
必须设 socket.binaryType = 'arraybuffer'
浏览器默认把 WebSocket 二进制帧当 Blob 传给 onmessage,而 protobuf-ts 和 protobufjs 的 decode() 都只认 Uint8Array 或 ArrayBuffer。漏设这行,后续所有解码都会失败。
- 连接建立后立即执行:
socket.binaryType = 'arraybuffer',不能晚于第一个send() - 服务端若发的是
BinaryWebSocketFrame(如 Netty)或 raw bytes(如 FastAPI 的bytes字段),前端不设 binaryType →event.data是Blob,需手动调blob.arrayBuffer(),多一次异步拷贝 - 用
protobuf-ts时,必须转成Uint8Array:PlayerPosition.decode(new Uint8Array(event.data));protobufjs老版本虽能接ArrayBuffer,但行为不一致,统一转Uint8Array更安全
int64 和 uint64 在 JS 里就是个坑
JS Number 最大安全整数是 2^53 - 1,int64 超出即精度丢失。后端传 9223372036854775807,前端收到可能是 9223372036854776000,再序列化回去就错位。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 协议设计阶段就禁用
int64/uint64,改用sint32(ZigZag 编码,负数也省字节)或string字段存 ID(仅当真需要 64 位 ID 时) - 高频字段如坐标、时间戳、计数器,一律用
sint32:-1 编码为 1 字节,int32则要 5 字节 - 若已有旧协议含
int64,前端必须用Long类型(protobufjs提供)或字符串中转,不能直赋 JS number
超 50 KB 的 Protobuf 消息必须分片,不是“建议”而是“必须”
单帧超过 50 KB,即使开了 permessage-deflate,也会放大弱网下的重传代价:丢一个 TCP 包,就得重传整帧。实测弱网下延迟抖动从 ±10ms 拉到 ±300ms。
- 发送前检查:
if (buffer.byteLength > 50 * 1024),超限则切片 - 每片加 2 字节头部:
Uint16Array([seq, total]),服务端据此重组;留 2 KB 空间,单片 payload ≤ 48 KB - 分片间用
setTimeout(..., 5)错开发送,防 burst 拥塞;首片发出后,服务端应立刻回 ACK,客户端才发下一片 - 服务端不要等全片收齐再处理——首片到达即可触发业务逻辑(如位置更新),避免卡顿
packed=true 不是可选项,是高频 repeated 字段的标配
比如玩家每秒上报 20 个 GPS 坐标点,repeated sint32 lat = 1; 默认每个值带独立 tag(1 字节)+ value,100 个点就是 200 字节;加 [packed=true] 后,tag 只出现一次,后面全是紧凑数值流,体积直接砍半。
- 所有高频
repeated数值字段(int32、sint32、bool)都显式加[packed=true] -
repeated string或repeated bytes不支持 packed,别硬加 - proto3 默认对数值 repeated 字段启用 packed,但 proto2 不默认,跨团队协作时务必显式声明,避免隐性差异
真正卡住 Protobuf + WebSocket 的,从来不是序列化库选哪个,而是二进制通道没设对、字段类型踩了 JS 精度雷、大消息硬塞不拆片——这些地方一错,错误表现就是静默丢包或解码崩溃,连日志都难抓。










