React组件无法接收Socket.io房间事件问题求助
排查玩家A无法接收Socket.io房间事件的问题
以下是针对你的场景的具体排查方向和修复建议:
1. 确认玩家A的Socket已成功加入目标房间
服务器端GAME_CREATE_ROOM事件中,先验证玩家A的Socket是否真的加入了对应房间:
// 服务器端修改GAME_CREATE_ROOM事件,添加日志 socket.on(PokeBattleSocketEvents.GAME_CREATE_ROOM, async (data) => { const game = await Game.findByPk(data.gameId); if (!game) { console.error('Game not found for ID:', data.gameId); return; } const roomId = game.dataValues.id; socket.join(roomId); // 打印日志确认加入情况 console.log(`Socket ${socket.id} joined room: ${roomId}`); // 额外发送测试事件,验证A是否能收到房间消息 socket.to(roomId).emit(PokeBattleSocketEvents.TEST_EVENT, { msg: 'Joined room successfully' }); });
同时在玩家A的Lobby组件中监听TEST_EVENT,看是否能收到这条测试消息,直接验证房间通信是否正常。
2. 修改服务器事件发送方式,排除范围问题
当前服务器用socket.to(data.roomId).emit(...),只会给房间内除当前Socket(玩家B)之外的成员发消息。如果玩家A的Socket不在房间里,自然收不到。可以先改成全局广播到房间的方式测试:
socket.on(PokeBattleSocketEvents.GAME_PLAYER_JOINING, async (data) => { socket.join(data.roomId); console.log("dummy log", data.roomId); // 改用io.to()给房间内所有成员发事件(包括B) app.io.to(data.roomId).emit(PokeBattleSocketEvents.GAME_PLAYER_JOINED, { 'player': data.userId, 'gameId': data.roomId }); });
如果这样玩家A能收到消息,说明之前的socket.to()本身没问题,但玩家A的Socket确实不在房间内,回到第一步排查加入逻辑。
3. 确保React组件的事件监听时机正确
即使把监听放到useEffect里,也要确保:
- 监听是在玩家A的Socket已经加入房间之后才注册的
- 组件渲染时不会重复注册或丢失监听
优化Lobby组件的监听逻辑:
export function Lobby() { const {socket} = useContext(SocketContext); const [player2, setPlayer2] = useState<string | null>(null); useEffect(() => { // 注册监听前先移除旧的,避免重复绑定 socket.off(PokeBattleSocketEvents.GAME_PLAYER_JOINED); const handlePlayerJoined = (data: {player: string, gameId: string}) => { console.log("player joined", data); toast.custom((t) => <Toast t={t} msg={`Player ${data.player} joined your lobby !`} />); setPlayer2(data.player); }; socket.on(PokeBattleSocketEvents.GAME_PLAYER_JOINED, handlePlayerJoined); console.log('GAME_PLAYER_JOINED listener registered'); return () => { socket.off(PokeBattleSocketEvents.GAME_PLAYER_JOINED, handlePlayerJoined); }; }, [socket]); return (<></>); }
打开浏览器控制台,确认GAME_PLAYER_JOINED listener registered日志在玩家A进入Lobby时打印,说明监听已正确注册。
4. 排查Socket重连导致的房间丢失问题
如果玩家A的Socket连接断开后重连,新的Socket实例不会自动加入之前的房间。在SocketProvider中添加重连处理:
export const SocketProvider = ({children}) => { const socket = io(import.meta.env.VITE_API_ENDPOINT); useEffect(() => { // 处理重连逻辑 const handleReconnect = () => { // 从本地存储获取玩家A当前的游戏ID,重新加入房间 const currentGameId = localStorage.getItem('currentGameId'); if (currentGameId) { socket.emit(PokeBattleSocketEvents.GAME_CREATE_ROOM, { gameId: currentGameId }); } }; socket.on('reconnect', handleReconnect); // 其他原有监听... socket.on(PokeBattleSocketEvents.TEST_EVENT, (data: object) => { console.log(data); }); return () => { socket.off('reconnect', handleReconnect); socket.off(PokeBattleSocketEvents.TEST_EVENT); socket.off(PokeBattleSocketEvents.GAME_CREATE_ROOM); }; }, []); // 其他原有逻辑... };
同时在玩家A创建游戏成功后,把gameId存入localStorage:
// 玩家A创建游戏的逻辑中添加 const createGame = async () => { const newGame = await gameCreate(...); localStorage.setItem('currentGameId', newGame.id); socket.emit(PokeBattleSocketEvents.GAME_CREATE_ROOM, { gameId: newGame.id }); navigate('/game/lobby/' + newGame.id); };
5. 验证事件名称的一致性
彻底检查前后端PokeBattleSocketEvents中GAME_PLAYER_JOINED的定义,确保拼写、大小写完全一致。比如前端枚举:
export enum PokeBattleSocketEvents { GAME_PLAYER_JOINED = 'GAME_PLAYER_JOINED', // 其他事件... }
后端必须使用完全相同的字符串,不能有任何差异。
内容的提问来源于stack exchange,提问作者Skunka
相关产品推荐
相关产品推荐

