You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 08:05:22