Vercel部署出现TypeError: server.listeners is not a function问题求助
解决Vercel部署时Socket.io的TypeError: server.listeners is not a function错误
问题描述
在Vercel部署应用时遇到TypeError: server.listeners is not a function错误,本地localhost环境运行正常,查阅相关问题未找到有效解决方案,以下是错误信息及后端代码。
错误信息
TypeError: server.listeners is not a function at Server.attach (/var/task/api/node_modules/engine.io/build/server.js:642:34) at attach (/var/task/api/node_modules/engine.io/build/engine.io.js:53:12) at Server.initEngine (/var/task/api/node_modules/socket.io/dist/index.js:293:43) ⛁ MongoDb is connected ✅
后端代码
import express from "express"; import mongoose from "mongoose"; import dotenv from "dotenv"; import authRoutes from "./routes/auth.route.js"; import blogRoutes from "./routes/blog.route.js"; import resumeRoutes from "./routes/resume.route.js"; import chatRoutes from "./routes/chat.route.js"; import cookieParser from "cookie-parser"; import path from "path"; import bodyParser from "body-parser"; import { fileURLToPath } from 'url'; import http from "http"; import { Server } from 'socket.io'; dotenv.config(); mongoose.connect(process.env.MONGO) .then(() => { console.log("⛁ MongoDb is connected ✅"); }) .catch((err) => { console.error(err); }); const app = express(); const server = http.createServer(app); const io = new Server(server); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser()); app.use("/api/auth", authRoutes); app.use("/api/blog", blogRoutes); app.use("/api/resume", resumeRoutes); app.use("/api/chats", chatRoutes); io.on('connection', (socket) => { console.log('a user connected'); socket.on('disconnect', () => { console.log('a user disconnected'); }); }); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const distDirectory = path.join(__dirname, '../client/dist'); app.use(express.static(distDirectory)); app.get('/', (req, res) => { res.sendFile(path.join(distDirectory, 'index.html')); }); app.use((err, req, res, next) => { const statusCode = err.statusCode || 500; const message = err.message || "Internal Server Error"; console.error(message); res.status(statusCode).json({ success: false, statusCode, message, }); }); server.listen(process.env.PORT || 3000, () => { console.log(`🚀 Server is running on port ${process.env.PORT || 3000} 🥳`); }); export default io;
解决方案
核心原因
Vercel是无服务器平台,采用请求驱动的实例模式,不支持长期运行的HTTP服务器。传统的http.createServer()+server.listen()模式与该平台逻辑冲突,导致Socket.io无法正确绑定服务器实例。
具体步骤
- 安装Socket.io的Vercel适配包
npm install @socket.io/vercel
- 创建Socket.io专用API路由
在项目中新建api/socket/io.js文件,内容如下:
import { Server } from "socket.io"; import { createServer } from "http"; import { VercelRequest, VercelResponse } from "@vercel/node"; const ioHandler = (req, res) => { // 复用服务器实例,避免重复初始化 if (!res.socket.server.io) { const server = createServer((req, res) => { res.writeHead(404); res.end(); }); const io = new Server(server, { path: "/api/socket/io", addTrailingSlash: false, }); io.on("connection", (socket) => { console.log("a user connected"); socket.on("disconnect", () => { console.log("user disconnected"); }); }); res.socket.server.io = io; } res.end(); }; export default ioHandler;
- 修改主服务器代码
移除原代码中的HTTP服务器创建、Socket.io初始化及server.listen逻辑,改为导出Express应用:
import express from "express"; import mongoose from "mongoose"; import dotenv from "dotenv"; import authRoutes from "./routes/auth.route.js"; import blogRoutes from "./routes/blog.route.js"; import resumeRoutes from "./routes/resume.route.js"; import chatRoutes from "./routes/chat.route.js"; import cookieParser from "cookie-parser"; import path from "path"; import bodyParser from "body-parser"; import { fileURLToPath } from 'url'; dotenv.config(); mongoose.connect(process.env.MONGO) .then(() => { console.log("⛁ MongoDb is connected ✅"); }) .catch((err) => { console.error(err); }); const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.json()); app.use(cookieParser()); app.use("/api/auth", authRoutes); app.use("/api/blog", blogRoutes); app.use("/api/resume", resumeRoutes); app.use("/api/chats", chatRoutes); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const distDirectory = path.join(__dirname, '../client/dist'); app.use(express.static(distDirectory)); app.get('/', (req, res) => { res.sendFile(path.join(distDirectory, 'index.html')); }); app.use((err, req, res, next) => { const statusCode = err.statusCode || 500; const message = err.message || "Internal Server Error"; console.error(message); res.status(statusCode).json({ success: false, statusCode, message, }); }); export default app;
- 配置Vercel路由规则
在项目根目录创建vercel.json文件,添加路由配置:
{ "routes": [ { "src": "/api/socket/io", "dest": "/api/socket/io.js", "methods": ["GET", "POST", "OPTIONS"] }, { "src": "/(.*)", "dest": "/api/index.js" } ] }
前端连接调整
前端连接Socket.io时,需要指向新的API路径:
import { io } from "socket.io-client"; const socket = io({ path: "/api/socket/io" });
内容的提问来源于stack exchange,提问作者MdNihal05
相关产品推荐
相关产品推荐

