websocket连接失败时onerror不触发且onopen不执行,主因是服务端未启动或跨域拦截;需手动添加超时判断,并注意开发用ws://、上线必用wss://。

WebSocket 连接失败:onerror 不触发,但 onopen 也不执行
这是最常被当成“代码没写错”却卡住的点——实际是服务端没启动或跨域拦截了。HTML5 游戏联机本质靠客户端主动连服务器,不是浏览器自动帮你配好通信链路。
-
WebSocket实例创建后,状态是0(CONNECTING),但若服务端无响应,它会卡在这儿,既不升到1(OPEN),也不会立刻进onerror - 必须手动加超时判断:
const ws = new WebSocket('ws://localhost:8080'); let timeout = setTimeout(() => { if (ws.readyState === WebSocket.CONNECTING) { console.error('WebSocket connection timed out'); } }, 5000); - 开发时用
ws://协议;上线必须换wss://,否则 Chrome 会直接拒绝连接(混合内容限制)
消息收发用 JSON.stringify() 还是 ArrayBuffer?
取决于你传的是游戏状态快照,还是高频输入指令。前者可读、易调试;后者省带宽、少解析开销,但写起来容易出错。
- 玩家移动、射击等操作建议用轻量 JSON:
ws.send(JSON.stringify({ type: 'input', player: 'p1', action: 'jump' })); - 每秒同步 30 帧的位置数据(x/y/rot)时,用
ArrayBuffer+DataView更稳:const buf = new ArrayBuffer(12); const view = new DataView(buf); view.setFloat32(0, x, true); view.setFloat32(4, y, true); view.setFloat32(8, rot, true); ws.send(buf);
- 别在
onmessage里直接JSON.parse(event.data)——如果服务端发来的是ArrayBuffer,event.data就不是字符串,会报Unexpected token
怎么让多个玩家画面同步不卡顿?
不是把所有帧都广播出去就完事。网络延迟、丢包、不同设备渲染帧率不一致,会导致“你看到的我总比实际慢半拍”。
- 服务端不做权威帧同步(即不每帧校验并重发)的话,客户端得自己做插值:
lerp(currentPos, targetPos, 0.1),而不是硬跳到最新位置 - 客户端预测要开启:收到输入指令后立刻本地执行,再等服务端确认;若确认结果和预测不一致(比如撞墙了),再回滚 + 补偿
- 别用
setInterval发送状态——用requestAnimationFrame驱动发送逻辑,和渲染节奏对齐,避免“发得太密塞死 WebSocket 缓冲区”
本地测试能连,部署后连不上:CORS 和代理配置漏了哪步?
浏览器不会报 CORS 错误给 WebSocket,它只在 HTTP 握手阶段被拦截,表现就是连接直接关闭,readyState 变成 CLOSED(3)。
立即学习“前端免费学习笔记(深入)”;
- 确保 Nginx 或 Cloudflare 没把
Upgrade和Connection头过滤掉:proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
- 服务端(如 Node.js 的
ws库)要显式允许来源:new WebSocket.Server({ noServer: true })配合自定义 upgrade 处理,否则默认只认*,而现代浏览器要求精确匹配 - 静态资源走
https://game.example.com,但 WebSocket 写成ws://game.example.com:8080——端口不同就算跨源,必须用wss://且后端同域名反代
真正难的不是连上,是连上之后怎么让两个设备上的小人看起来“同时起跳”。时间戳对齐、RTT 估算、输入延迟补偿……这些没法靠改一两行代码解决,得在每次 onmessage 回调里悄悄做点手脚。











