Node.js服务器与前端Socket.io连接失败问题求助
Socket.io连接失败问题排查与修复方案
核心问题分析与修复步骤
1. 前端Socket连接地址错误
前端代码中io("/")会默认连接当前页面的域名(即前端服务的http://localhost:3001),但后端Socket.io服务运行在3000端口,必须显式指定后端地址:
// SocketContext.js 中修改连接代码 const socket = io("http://localhost:3000", { query: { userId: user?._id, }, });
2. 完善CORS配置
后端Socket.io的CORS配置需要覆盖完整请求方法,若前端需传递Cookie等凭证,需开启credentials:
// socket.js 中修改CORS配置 const io = new Server(server, { cors: { origin: "http://localhost:3001", methods: ["GET", "POST", "PUT", "DELETE"], credentials: true // 根据业务需求添加,允许携带凭证 }, });
3. 修复Express实例重复创建问题
原代码中socket.js独立创建Express实例,导致server.js中配置的中间件、路由无法作用于Socket.io绑定的HTTP服务器。调整为复用server.js的Express实例:
修改socket.js:
import { Server } from "socket.io"; import http from "http"; import Message from "../models/messageModel.js"; import Conversation from "../models/conversationModel.js"; // 接收外部传入的Express实例 export const initSocket = (app) => { const server = http.createServer(app); const io = new Server(server, { cors: { origin: "http://localhost:3001", methods: ["GET", "POST", "PUT", "DELETE"], credentials: true }, }); const userSocketMap = {}; // userId: socketId export const getRecipientSocketId = (recipientId) => { return userSocketMap[recipientId]; }; io.on("connection", (socket) => { console.log("User connected: ", socket.id); const userId = socket.handshake.query.userId; // 修复userId判断逻辑,避免字符串"undefined"被误判 if (userId && userId !== "undefined") { userSocketMap[userId] = socket.id; socket.userId = userId; // 绑定userId到socket对象,解决闭包问题 } io.emit("getOnlineUsers", Object.keys(userSocketMap)); socket.on("markMessagesAsSeen", async ({ conversationId, userId }) => { try { await Message.updateMany( { conversationId: conversationId, seen: false }, { $set: { seen: true } } ); await Conversation.updateOne( { _id: conversationId }, { $set: { "lastMessage.seen": true } } ); const recipientSocketId = userSocketMap[userId]; if (recipientSocketId) { io.to(recipientSocketId).emit("messagesSeen", { conversationId }); } } catch (error) { console.log(error); } }); socket.on("disconnect", () => { console.log("user disconnected"); // 使用socket.userId避免闭包导致的userId失效 if (socket.userId) { delete userSocketMap[socket.userId]; io.emit("getOnlineUsers", Object.keys(userSocketMap)); } }); }); return { io, server }; };
修改server.js:
import express from 'express'; import dotenv from 'dotenv'; import cookieParser from 'cookie-parser'; import connectDB from './databaseConnection/connectDatabase.js'; import userRoutes from "./routes/userRoutes.js"; import postRoutes from "./routes/postRoutes.js"; import messageRoutes from "./routes/messageRoutes.js"; import { v2 as cloudinary } from "cloudinary"; import { initSocket } from './socket/socket.js'; dotenv.config(); connectDB(); cloudinary.config({ cloud_name: process.env.CLOUDINARY_CLOUD_NAME, api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET, }); // 创建Express实例 const app = express(); // Middlewares app.use(express.json({ limit: "50mb" })); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); // Routes app.use("/api/users", userRoutes); app.use("/api/posts", postRoutes); app.use("/api/messages", messageRoutes); // 初始化Socket.io并获取server const { server } = initSocket(app); server.listen(3000, () => { console.log(`Server listening at http://localhost:3000`); });
4. 添加前端连接错误监听
在前端代码中添加错误监听,便于定位具体连接失败原因:
// SocketContext.js 中添加错误监听 useEffect(() => { if (!user?._id) return; // 用户未登录时不创建连接 const socket = io("http://localhost:3000", { query: { userId: user._id, }, }); socket.on("connect_error", (err) => { console.error("Socket连接失败:", err); }); socket.on("connect", () => { console.log("Socket连接成功:", socket.id); }); socket.on("getOnlineUsers", (users) => { setOnlineUsers(users); }); setSocket(socket); return () => socket.close(); }, [user?._id]);
5. 验证版本一致性
再次确认前后端Socket.io版本完全一致,执行以下命令检查:
# 后端查看版本 npm list socket.io # 前端查看版本 npm list socket.io-client
确保两个版本号完全相同,若不同则统一升级或降级到同一版本。
内容的提问来源于stack exchange,提问作者Karan Kaushal
相关产品推荐
相关产品推荐

