WebSocket游戏同步需服务端权威校验、客户端预测插值、消息带序列号与时间戳;服务端须验证移动合法性并限制速度,客户端需本地预测+基于serverTime的线性插值,广播应按视野范围切分而非全服。

WebSocket 实现游戏同步,关键不在“连上就行”,而在于服务端是否权威、客户端是否做预测与插值、消息是否带序列号和时间戳——否则玩家看到的位置跳变、技能延迟、状态不一致全是必然结果。
服务端必须做权威校验,不能信任客户端位置
玩家移动指令(如 {"type":"move","dir":"right"})只是请求,不是最终状态。服务端收到后要验证:是否在地图内、是否撞墙、是否超出移动速度上限、CD 是否就绪。只有校验通过才更新权威位置,并广播给其他人。
- 直接把客户端发来的
x/y原样存进服务端状态 = 开挂入口 - 服务端应维护每个玩家的
lastUpdateTime和最大允许位移(如每秒 100px),用时间差 + 速度推算合法范围 - 若客户端上报位置明显超速(比如 50ms 内横移 200px),直接丢弃并可标记为可疑连接
客户端必须做本地预测 + 插值,不能等服务端回传才动
纯等待服务端广播会导致操作延迟感极强。正确做法是:按键瞬间本地立刻移动角色,同时发指令;服务端校验后回传权威位置(含 serverTime 和 seq),客户端据此做线性插值(Lerp)或回滚修正。
- 未加预测时,200ms 网络延迟 = 角色响应慢半拍;加预测后,延迟感基本消失
- 插值目标不是“上一帧位置”,而是服务端回传的带
serverTime的位置,用lerp(prev, next, (now - prevTime) / (nextTime - prevTime)) - 收到乱序包(
seq小于已处理最大值)直接丢弃,避免画面倒退
广播必须按范围切分,别一股脑 @SendTo("/topic/all")
全服广播位置更新在 1000 人局里等于自毁:单个玩家每秒 30 帧 × 1000 人 = 3 万条消息。实际只需广播给“同房间”或“视野半径内”的玩家。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 服务端需维护空间索引结构,如二维网格或四叉树,
getPlayersInRadius(x, y, 200)才能快速查出目标列表 - 技能释放类消息(
{"type":"cast","skill":"lightning"})只广播给距离 - 广播前务必过滤发送者自身连接,否则客户端会重复处理自己刚发的指令
重连与状态恢复不能只靠“重新 joinRoom”
断线重连后,如果只让玩家重新发 {"type":"joinRoom","roomId":"r123"},就会丢失中间 3 秒的状态变更,角色凭空消失或技能冷却错乱。
- 服务端需为每个房间保存最近 N 条关键状态快照(如最后 5 次位置更新 + 技能释放),重连时一并推送
- 客户端重连成功后,先清空本地预测状态,再用快照重建,再开启新预测周期
- 连接建立后立即发心跳(
{"type":"ping","ts":1748179740123}),服务端回pong并附带当前房间权威gameTime,用于后续插值对齐
最容易被忽略的是:服务端禁用 Nagle 算法(socket.setNoDelay(true))和客户端消息节流——哪怕逻辑全对,一个 TCP 小包合并延迟就能让 30FPS 变成幻灯片。










