新用户连接时socket.username被设为首个用户,其他用户该值为undefined的问题
问题排查与修复
核心问题
- 认证中间件注册位置错误:你把
io.use()放在了io.on('connection')的回调里,这会导致每有新用户连接就重复注册一次中间件,后续连接的中间件逻辑会混乱,出现用户名被覆盖或未赋值的情况。 - 房间映射变量作用域错误:
socketRoomMap和socketObject定义在connection回调内部,每个新连接都会创建新的Map/对象,之前用户的房间记录会丢失,也无法跨连接共享数据。
修复后的完整代码
const http = require('http'); const ioServer = require('socket.io'); const jwt = require('jsonwebtoken'); const app = require('./routes/user.routes'); const mongoConnect = require('../database/mongo'); const User = require('../database/models/user.model'); require('dotenv').config(); const server = http.createServer(app); const io = ioServer(server, { cors: { origin: "*" } }); // 全局认证中间件:放在io.on('connection')外面,只注册一次 io.use(async (socket, next) => { try { const token = socket.handshake.auth.token; const decoded = jwt.verify(token, process.env.SECRET_KEY); const user = await User.findById(decoded.userId); if (!user) { throw new Error('User not found'); } socket.username = user.username; console.log('user', socket.username); next(); } catch (error) { console.error('Authentication error', error); next(new Error('Authentication error')); } }); // 全局房间映射:所有连接共享同一个Map,放在connection外面 const socketRoomMap = new Map(); const socketObject = {}; io.on('connection', (socket) => { // Handle 'join' event when a client joins the chat room socket.on('join', (room) => { socket.join(room); socketRoomMap.set(socket.username, room); socket.emit('joined', `You joined ${room}`); socket.broadcast.emit('user joined', `${socket.username} joined ${room}`); }); // Handle 'chat message' event when a client sends a message socket.on('chat message', (room, message) => { io.to(room).emit('chat message', `${socket.username}: ${message}`); }); // Handle 'disconnect' event when a client disconnects socket.on('disconnecting', () => { const room = socketRoomMap.get(socket.username); if (room) { socket.broadcast.to(room).emit('user left', `${socket.username} left the chat room`); socketRoomMap.delete(socket.username); } }); }); server.listen(3000, () => { mongoConnect() .then(() => { }) .catch((error) => { console.error('MongoDB connection error:', error); }); console.log('Server started running...'); }); process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error.stack); process.exit(1); });
关键修改说明
- 调整中间件位置:把
io.use()移到io.on('connection')外部,作为Socket.io的全局中间件,确保每个连接只会触发一次认证逻辑,正确给socket.username赋值。 - 调整变量作用域:将
socketRoomMap和socketObject提升到全局作用域,这样所有客户端连接都共用同一个映射表,不会出现房间记录丢失的问题。
内容的提问来源于stack exchange,提问作者Hayatudeen Abdulrahman
相关产品推荐
相关产品推荐

