如何在Express中同时处理HTTP请求与Socket.IO连接?
Express与Socket.IO共存的正确实现方案
核心问题原因
Socket.IO的连接握手请求会先经过Express的中间件链,你设置的app.all("*")通配路由会拦截所有未匹配的HTTP请求,包括Socket.IO命名空间的握手请求,从而触发404错误。
解决方案步骤
1. 用HTTP Server同时挂载Express和Socket.IO
必须通过http.createServer创建HTTP服务器,同时挂载Express应用和Socket.IO,不能直接用app.listen()启动服务。
import express from 'express'; import { createServer } from 'http'; import { Server } from 'socket.io'; const app = express(); // 创建HTTP服务器并绑定Express const httpServer = createServer(app); // 初始化Socket.IO并挂载到HTTP服务器 const io = new Server(httpServer, { cors: { origin: "*" } }); // 处理/socket命名空间的连接事件 io.of("/socket").on("connection", (socket) => { console.log('客户端已连接到/socket命名空间'); // 示例:监听客户端消息事件 socket.on('sendMsg', (data) => { console.log('收到客户端消息:', data); socket.emit('recvMsg', '服务器已接收你的消息'); }); socket.on('disconnect', () => { console.log('客户端断开连接'); }); });
2. 在Express通配路由前添加跳过逻辑
在app.all("*")之前,新增一个中间件,判断请求路径是否属于Socket.IO的命名空间,若是则跳过后续Express中间件(包括404路由),直接交给Socket.IO处理。
// 跳过Socket.IO命名空间路径的Express处理 app.use((req, res, next) => { // 匹配你的Socket.IO命名空间路径(这里是/socket) if (req.path.startsWith('/socket')) { // 跳过后续路由中间件,转交Socket.IO处理 return next('route'); } next(); }); // 你的REST路由 app.use("/users", userRouter); app.use("/products", productRouter); // 404错误处理路由(必须放在所有路由之后) app.all("*", async (req: Request, res: Response) => { throw new NotFoundError("route"); });
3. 启动HTTP服务器而非Express应用
最后启动的是HTTP服务器,而非直接启动Express应用:
const PORT = 3000; httpServer.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); });
客户端连接示例
客户端需要指定对应的命名空间进行连接:
import { io } from 'socket.io-client'; // 连接到/socket命名空间 const socket = io('http://localhost:3000/socket'); // 示例:发送消息到服务器 socket.emit('sendMsg', 'Hello Socket.IO!'); // 接收服务器回复 socket.on('recvMsg', (data) => { console.log('服务器回复:', data); });
关键注意事项
- 必须使用
httpServer.listen()启动服务,否则Socket.IO无法绑定到HTTP服务器完成握手。 - 跳过逻辑的中间件必须放在所有REST路由和404路由之前,确保Socket.IO路径不会被Express拦截。
- 生产环境中建议修改CORS配置,替换
origin: "*"为具体允许的域名,避免安全风险。
内容的提问来源于stack exchange,提问作者ACEY
相关产品推荐
相关产品推荐

