从「看不懂 WebSocket」到「被 WebSocket 折磨」——我的多人游戏开发踩坑记录
最近在开发一个多人在线骰子游戏项目时,我第一次真正深入接触 WebSocket。
以前学前端的时候,我一直觉得:
-
登录用 HTTP
-
获取数据用 HTTP
-
提交表单用 HTTP
好像整个项目都离不开 HTTP。
直到开始做实时对战游戏,我才发现:
HTTP 根本解决不了所有问题。
为什么需要 WebSocket?
想象这样一个场景:
玩家 A 创建房间。
玩家 B 通过房间号加入。
玩家 C 进入房间。
玩家 D 准备开始游戏。
如果使用传统 HTTP:
前端只能不断轮询:
setInterval(() => {
fetch('/room/status')
}, 1000)
每秒请求一次服务器。
这样会产生几个问题:
1. 数据不实时
如果轮询间隔是 1 秒:
玩家 B 已经进入房间,
但玩家 A 可能需要等待 1 秒后才能看到。
2. 请求浪费
即使房间什么都没发生:
前端仍然在不断发送请求。
3. 多人场景压力大
如果:
-
1000 个房间
-
每个房间 4 个人
那么服务器会收到大量无意义请求。
因此就需要 WebSocket。
WebSocket 到底是什么?
第一次接触的时候我一直没理解。
后来我把它理解成:
HTTP 是打电话。
每次有事:
-
拨号
-
通话
-
挂断
下一次再重新拨号。
而 WebSocket 更像是:
建立一个微信群。
连接成功后:
服务器和客户端一直保持连接。
任何一方有消息:
直接发送。
无需重新建立连接。
所以它特别适合:
-
在线聊天
-
多人游戏
-
实时排行榜
-
股票行情
-
在线协同编辑
这些实时场景。
我项目中的 WebSocket 流程
在我的骰子游戏里:
创建房间
房主调用接口:
POST /match/create
返回:
{
"roomId": "123456"
}
随后连接:
ws://server/room/123456
加入房间频道。
玩家加入房间
玩家输入房间号:
POST /match/join
加入成功后:
同样连接:
ws://server/room/123456
这样所有玩家都在同一个频道。
房间状态同步
有人进入房间:
服务器推送:
{
"type": "player_join",
"player": {
"id": 2,
"name": "Tom"
}
}
所有客户端收到消息:
socket.onmessage = (event) => {
const data = JSON.parse(event.data)
}
然后更新玩家列表。
无需刷新页面。
我遇到的第一个坑:疯狂重连
这个问题折磨了我很久。
现象:
控制台不断打印:
WebSocket Connected
WebSocket Closed
WebSocket Connected
WebSocket Closed
疯狂循环。
后来排查发现:
React 的 useEffect 依赖写错了。
类似这样:
useEffect(() => {
connect()
}, [players])
而 players 又会被 WebSocket 消息更新。
于是:
收到消息
↓
players变化
↓
effect重新执行
↓
关闭旧连接
↓
建立新连接
↓
再次收到消息
↓
继续循环
最终形成无限重连。
这是我第一次真正意识到:
React 状态更新和 WebSocket 生命周期之间存在强关联。
第二个坑:连接成功了,但消息收不到
当时我以为:
连接成功 = 功能正常。
后来发现:
并不是。
可能出现:
WebSocket OPEN
但实际上:
-
加入了错误房间
-
房间号传错
-
用户身份错误
结果:
连接虽然存在,
却永远收不到真正的数据。
所以后来我都会验证:
socket.send({
type: 'ping'
})
确认服务器确实能正常响应。
我对 WebSocket 的理解
以前我觉得:
WebSocket 是一个高级技术。
后来发现:
它本质上只是解决一个问题:
让服务器主动通知客户端。
如果项目不需要实时性:
HTTP 完全够用。
如果项目涉及:
-
多人协作
-
在线聊天
-
实时游戏
那么 WebSocket 基本是必选项。
总结
这次开发多人骰子游戏,我最大的收获不是学会了 WebSocket API。
而是真正理解了:
前端不仅仅是在写页面。
当项目开始涉及多人协作、实时通信、状态同步时,
就必须开始理解客户端、服务器以及连接生命周期之间的关系。
WebSocket 对我来说不再只是一个技术名词。
它是我第一次真正接触实时系统开发的起点。
而那些连接失败、无限重连、消息丢失的坑,
也成了这次项目里最深刻的学习经历。
更多推荐

所有评论(0)