Socket.io中getUser(receiverId)返回undefined问题排查修复
Socket.io查询指定用户返回undefined问题修复方案
问题复现
开发React实时聊天应用时,使用Socket.io实现定向消息/通知推送,传入receiverId调用用户查询方法时始终返回undefined,触发报错。问题出在friendRequest事件处理逻辑中,对应代码片段:
socket.on("friendRequest", ({ senderId, receiverId }) => { const receiver = getUser(receiverId); console.log(receiver) });
服务端完整代码如下:
const express = require('express'); const socketio = require('socket.io'); const http = require('http'); const cors = require('cors'); const PORT = process.env.PORT || 5000; const router = require('./router'); const app = express(); const server = http.createServer(app); const io = socketio(server); app.use(router); app.use(cors()); let users = []; const addUser = (userId, socketId) => { !users.some((user) => user.userId === userId) && users.push({ userId, socketId }); }; const removeUser = (socketId) => { users = users.filter((user) => user.socketId !== socketId); }; const getUser = (userId) => { return users.find((user) => user.userId === userId); }; io.on('connect', (socket) => { console.log("a user connected."); socket.on("addUser", (userId) => { addUser(userId, socket.id); io.emit("getUsers", users); }); socket.on("sendNotification", ({ senderId, receiverId, type }) => { const receiver = getUser(receiverId); io.to(receiver.socketId).emit("getNotification", { senderId, type, }); }); socket.on("friendRequest", ({ senderId, receiverId }) => { const receiver = getUser(receiverId); console.log(receiver) }); socket.on("sendText", ({ senderId, receiverId, text }) => { const receiver = getUser(receiverId); io.to(receiver.socketId).emit("getText", { senderId, text, }); }) socket.on("disconnect", () => { console.log("User Disconnected", socket.id); removeUser(socket.id); io.emit("getUsers", users); }); }); app.use(router); server.listen(PORT,() => console.log('Server has started on port ${PORT}'));
根因分析
getUser(receiverId)返回undefined的核心原因是:传入的receiverId无法和在线用户列表users中的记录匹配,常见触发场景包括:
- 类型不匹配:
getUser方法用全等===做匹配,若接收方上线时存的userId是数字类型,前端传的receiverId是字符串类型,会直接判定为不相等 - 用户未注册上线:接收方建立socket连接后,没有触发
addUser事件上报自身userId,服务端在线列表中根本没有该用户的记录 - 重复连接覆盖:同一账号多端登录、重复建立socket连接时,原有addUser逻辑不会清理旧记录,可能出现userId和socketId映射错乱
- 代码中存在附加bug:服务端启动日志使用单引号包裹模板字符串,插值语法
${PORT}不会生效,会误导端口排查
修复方案
- 统一参数类型,增加空值校验,修改所有涉及定向推送的事件逻辑,避免读取undefined属性报错。以
friendRequest事件为例,sendNotification、sendText事件按相同逻辑修改:
socket.on("friendRequest", ({ senderId, receiverId }) => { // 统一转成字符串类型,和存储的userId类型保持一致,若业务中userId是数字则转Number const targetUserId = String(receiverId); const receiver = getUser(targetUserId); // 查不到用户直接返回,不执行后续推送逻辑 if (!receiver) { console.log(`用户${targetUserId}当前不在线,无有效socket连接`); return socket.emit("errorMsg", "对方当前不在线,请求发送失败"); } // 定向推送逻辑 io.to(receiver.socketId).emit("getFriendRequest", { senderId }); console.log("匹配到接收用户:", receiver); });
- 检查前端连接逻辑:
- socket连接建立成功后,必须第一时间触发
addUser事件,传入当前登录用户的真实userId,完成上线注册 - 所有触发推送事件时传入的
receiverId,必须和接收方上报的userId值、类型完全一致
- socket连接建立成功后,必须第一时间触发
- 修复模板字符串bug,将启动日志的单引号替换为反引号:
server.listen(PORT,() => console.log(`Server has started on port ${PORT}`));
- 优化
addUser逻辑,解决同一userId重复映射问题:
const addUser = (userId, socketId) => { // 先移除该userId对应的旧连接记录,避免映射错乱 users = users.filter(user => user.userId !== userId); users.push({ userId, socketId }); };
内容的提问来源于stack exchange,提问作者Manou
相关产品推荐
相关产品推荐

