WebSocket 的 onerror 仅在连接建立前出错时触发,网络中断等断开场景走 onclose;需检查 event.code(如1006异常关闭)和 reason 判断重连策略。

WebSocket 连接失败时 onerror 不触发?检查 onclose 和状态码
HTML5 游戏用 WebSocket 传实时数据最常见,但很多人发现连接断开时 onerror 像“失灵”一样没反应。这不是 bug,是规范行为:onerror 只在连接建立前出错(比如 DNS 失败、SSL 握手失败)才触发;而网络中断、服务端主动关闭、超时等,都会走 onclose 回调。
实操建议:
立即学习“前端免费学习笔记(深入)”;
-
onclose里必须读event.code和event.reason—— 比如1006表示异常关闭(无明确原因),1001是服务端主动退出,这对判断是否要重连很关键 - 别只依赖
readyState === WebSocket.OPEN判断可用性,它可能卡在CONNECTING或已断开但未触发回调;加个简单心跳(例如每 5 秒发"ping",服务端回"pong")更可靠 - 浏览器对同一域名的
WebSocket并发数有限制(通常 6 个),游戏里多个模块各自建连容易撞上限,统一用单例管理连接
上传二进制资源(如截图、存档)用 ArrayBuffer 而不是 JSON.stringify
玩家上传本地生成的游戏截图(Blob)、序列化后的关卡数据(Uint8Array)时,如果转成 base64 字符串再塞进 JSON,体积膨胀约 33%,且 JS 引擎解析压力大,低端设备易卡顿。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 直接用
WebSocket.send(arrayBuffer)或fetch()配合FormData.append("file", blob, "save.dat")—— 后者服务端能按 multipart/form-data 解析,兼容性更好 - 若必须走 WebSocket,约定二进制协议头:前 4 字节为
Int32类型标识(如1= 存档,2= 截图),后面紧跟数据体,避免字符串解析开销 - Chrome/Firefox 支持
WebSocket.binaryType = "arraybuffer",但 Safari 旧版本只认"blob",上线前务必在真机测 iOS
fetch() 上传大文件被中断?设置 signal 和手动分片
用 fetch() 上传几十 MB 的录像回放或地图资源时,用户切到后台、锁屏、或网络波动,请求常静默失败,catch 也不抛错 —— 这是浏览器对长时间挂起请求的主动终止策略。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 给
fetch()加{ signal: AbortSignal.timeout(30_000) }明确超时,比依赖默认行为更可控 - 超过 5MB 的文件,别一股脑传:用
File.slice()分块(如每块 512KB),每块单独fetch,服务端做合并;失败只重传当前块,不拖累全局 - 分片上传必须带校验:客户端计算每块
SHA-256(可用SubtleCrypto.digest()),服务端比对,防止中间篡改或传输错位
服务端返回非 JSON 数据(如 Protocol Buffer)怎么在前端解码?
游戏对带宽和解析性能敏感,很多后端会用 Protocol Buffer 或 FlatBuffers 返回数据,但浏览器原生不支持,直接 response.arrayBuffer() 拿到的是一串字节,不是对象。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 确认服务端响应头含
Content-Type: application/x-protobuf,避免被当成文本自动 decode 成乱码 - 用官方
@protobufjs/protobuf库(轻量,仅 12KB)配合预编译的.proto文件生成 JS 类;别现场 parse .proto 文本,构建时完成 - FlatBuffers 更快但调试难:JS 端用
new GameData(rootAsGameData(responseArrayBuffer))直接读内存,不拷贝,但字段名写错不会报错,只返回undefined—— 建议开发期加一层字段存在性校验工具函数
真正麻烦的从来不是“怎么连上”,而是连上之后,哪次心跳没回、哪块分片丢了、哪个 protobuf 字段悄悄改了类型——这些细节不打日志、不写断言,上线后就只能靠玩家截图里的错误码反推。











