NodeJS+Socket.io WebSocket连接后立即断开问题求助
问题描述
后端基于NodeJS开发,前端采用NextJS,尝试建立WebSocket连接时,服务器刚完成连接就立即断开Socket。刷新前端页面后,服务器日志输出:
user connected ---- OKGG9r0qvvBs0EWeAAAB test@test.com socket disconnected--- OKGG9r0qvvBs0EWeAAAB test@test.com
服务端代码
import express from "express"; import dotenv from "dotenv"; import http from "http"; import { Server, Socket } from "socket.io"; import userRoutes from "./routes/users"; import { DefaultEventsMap } from "socket.io/dist/typed-events"; dotenv.config(); const PORT = process.env.PORT || 5000; const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: process.env.CLIENT_URL, }, }); app.use(express.json()); app.get("/ping", (req, res) => res.status(200).send("Pong")); app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "http://localhost:3000"); res.header("Access-Control-Allow-Credentials", "true"); res.header("Access-Control-Allow-Methods", "POST, GET"); res.header( "Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" ); next(); }); interface SType extends Socket { email?: string; } io.use((socket: SType, next) => { const userEmail = socket.handshake.auth.email; if (!userEmail) { return next(new Error("Invalid email")); } console.log("userEmail-----", userEmail); socket.email = userEmail; next(); }); io.on("connection", (socket) => { const users = []; for (let [id, socket] of io.of("/").sockets) { users.push({ userID: id, email: (socket as any).email, }); socket.emit("users", users); } socket.broadcast.emit("user connected", { userID: socket.id, email: (socket as any).email, }); console.log("user connected ---- ", socket.id, (socket as any).email); socket.on("disconnect", () => { console.log("socket disconnected---", socket.id, (socket as any).email); (socket as any).email = ""; }); }); app.use("/users", userRoutes); server.listen(PORT, () => console.log(`server running on port ${PORT}`));
客户端代码
useEffect(() => { if (userState) { socket.auth = { email: userState.email }; } }, [userState]); useEffect(() => { socket.on("users", (users: any) => { console.log("users", users); setUsers(users); }); socket.on("user connected", (user: any) => { setUsers((previousState: any) => [...previousState, user]); }); }, [socket]);
已尝试Stack Overflow和Socket.io官方文档的方案,但均无效,求问题原因及解决办法。
问题排查与解决
CORS配置冲突:服务端同时配置了Socket.io的
cors选项和Express的CORS中间件,规则冲突可能导致连接异常。建议移除Express的CORS中间件,仅保留Socket.io的配置,确保process.env.CLIENT_URL正确设置为http://localhost:3000(与前端地址一致)。客户端Socket auth传递时机错误:客户端在
useEffect中事后赋值socket.auth,若此时Socket已发起连接,auth信息无法被正确传递。需在Socket实例创建时直接传入auth参数:const socket = io("http://localhost:5000", { auth: { email: userState?.email } });若
userState为异步获取,需在userState就绪后创建Socket,或赋值后调用socket.connect()重新连接:useEffect(() => { if (userState) { socket.auth = { email: userState.email }; socket.connect(); } }, [userState]);服务端变量覆盖问题:
connection事件处理中,遍历sockets时重复使用变量名socket,覆盖了当前连接的Socket对象,导致后续逻辑异常触发断开。修改循环变量名:for (let [id, clientSocket] of io.of("/").sockets) { users.push({ userID: id, email: (clientSocket as any).email, }); clientSocket.emit("users", users); }NextJS Socket实例管理问题:若Socket实例全局创建,页面刷新或路由切换时易出现重复连接/断开。建议在组件内创建实例,并在组件卸载时主动断开:
useEffect(() => { const socket = io("http://localhost:5000", { auth: { email: userState?.email } }); socket.on("users", (users) => { setUsers(users); }); socket.on("user connected", (user) => { setUsers(prev => [...prev, user]); }); return () => { socket.disconnect(); }; }, [userState]);
内容的提问来源于stack exchange,提问作者Tirlochan Arora
相关产品推荐
相关产品推荐

