socket.io服务端向客户端第二次emit事件客户端无法接收问题求助
问题根因
- 核心错误是你把
app.post('/player_connect')路由的定义写在了io.on('connection')的回调内部:express的路由是全局单例的,只有第一次有客户端连接时会注册成功该路由,后续所有新客户端连接时重复注册的路由都会被忽略,路由内部捕获到的socket变量永远是第一个连接的客户端实例,后续客户端触发该接口时,消息只会发给第一个连接的客户端,其他客户端都收不到。 - 客户端
useEffect监听socket事件没有添加清理函数,组件卸载/重渲染时会重复绑定事件监听器,可能导致消息处理异常。 - 滥用
await调用socket.emit:socket.emit是同步方法,不需要await修饰。
修复方案
服务端代码修改
首先把路由定义挪到io.connection外部,如果是全量通知所有在线玩家的场景直接用io.emit即可,需要定向给特定用户发消息可以自行维护socket连接池:
// 可选:全局socket连接池,需要定向发消息时可以使用 const connectedSockets = new Map() io.on('connection', async function (socket) { const socketId = socket.id; connectedSockets.set(socketId, socket) const mta = new Client("20.64.24.144", 22005, "*", "*"); mta.resources.evokestats.getPlayerCount() .then((result) => { console.log("result", result); socket.emit("players-start", { players: result }) }) .catch((err) => { console.error(`Ooops! Something went wrong ${err}`); }); // 连接断开时清理连接池 socket.on('disconnect', () => { connectedSockets.delete(socketId) }) }); // 路由挪到connection回调外,全局仅注册一次 app.post('/player_connect', async function (req, res) { const ip = req.body[0]; const player = await players.findOne({ ip: ip }) const newPlayerCount = req.body[1] if (player) { // 给所有在线客户端广播消息 io.emit("players", { players: newPlayerCount }) } else { try { await players.create({ ip: ip, name: req.body[2] }) io.emit("players", { players: newPlayerCount }) io.emit("last_24_players", { players: 1 }); } catch (error) { console.log("error", error) } } res.send("connected") });
客户端代码修改
给useEffect添加清理函数,避免重复绑定事件:
useEffect(() => { const handlePlayersStart = (data) => { setNowPlayers(data.players) } const handlePlayers = (data) => { console.log("players", data) setNowPlayers(data.players) } socket.on("players-start", handlePlayersStart); socket.on("players", handlePlayers); // 组件卸载时移除事件监听 return () => { socket.off("players-start", handlePlayersStart) socket.off("players", handlePlayers) } }, [])
内容的提问来源于stack exchange,提问作者Derlys Fernando
相关产品推荐
相关产品推荐

