Socket IO仅在开发模式可用,Heroku生产环境连接超时问题
问题:Heroku生产环境下Socket.io消息组件无法连接,开发环境正常
开发的应用内消息组件在本地开发模式下运行正常,但部署到Heroku生产环境后完全无法使用,浏览器控制台报错:
GET https://www.example.com:8900/socket.io/?EIO=4&transport=polling&t=Oftd3RH net::ERR_CONNECTION_TIMED_OUT
服务端代码
const express = require("express"); const app = express(); const cors = require("cors"); app.use(cors()); // ...routes // app.use(...routes) const PORT = process.env.PORT || 5000; const IO_PORT = process.env.IO_PORT || 8900; const MODE = process.env.NODE_ENV; app.listen( PORT, console.log( `Server running in ${MODE} mode on port ${PORT}`.yellow.underline.bold, ), ); const io = require("socket.io")(IO_PORT, { cors: { origin: `${process.env.CLIENT_URL}`, // dev: http://localhost:3000, prod: https://www.example.com }, }); io.on("connection", (socket) => { // console.log(`user connected: ${socket.id}`); }
客户端代码
import React, { useEffect, useRef, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { io } from "socket.io-client"; const MessageComponent = () => { const socket = useRef(); useEffect(() => { socket.current = io(`ws://${window.location.hostname}:8900`); // listening to events }); }
问题原因
- Heroku端口限制:Heroku仅为每个应用分配一个动态端口,不允许开放多个独立端口。你的代码让Socket.io单独监听8900端口,这个端口在生产环境无法被外部访问,导致连接超时。
- Socket.io与Express服务器分离:当前代码将Socket.io和Express服务器分别启动在不同端口,不符合Heroku的部署规则。
- 客户端连接配置错误:生产环境使用HTTPS,但客户端用
ws://明文协议,且硬编码了8900端口,无法适配Heroku的路由机制。
解决方案
1. 整合Socket.io到Express主服务器
将Socket.io挂载到已有的Express HTTP服务器上,共享Heroku分配的同一个端口:
const express = require("express"); const app = express(); const cors = require("cors"); app.use(cors()); // ...routes // app.use(...routes) const PORT = process.env.PORT || 5000; const MODE = process.env.NODE_ENV; // 先创建HTTP服务器实例 const server = app.listen( PORT, console.log( `Server running in ${MODE} mode on port ${PORT}`.yellow.underline.bold, ), ); // 将Socket.io绑定到已启动的HTTP服务器 const io = require("socket.io")(server, { cors: { origin: process.env.CLIENT_URL, // 保持环境变量配置 }, }); io.on("connection", (socket) => { console.log(`user connected: ${socket.id}`); // 你的Socket事件逻辑 });
2. 修改客户端连接逻辑
去掉硬编码的协议和端口,让Socket.io自动适配当前环境的域名和协议:
import React, { useEffect, useRef } from "react"; import { io } from "socket.io-client"; const MessageComponent = () => { const socket = useRef(); useEffect(() => { // 空字符串表示自动使用当前页面的域名、端口和协议 socket.current = io("", { transports: ["websocket", "polling"] // 优先使用WebSocket,失败则降级到polling }); // 监听连接状态 socket.current.on("connect", () => { console.log("Socket连接成功"); }); socket.current.on("disconnect", () => { console.log("Socket断开连接"); }); // 组件卸载时清理连接 return () => { socket.current.disconnect(); }; }, []); // 空依赖确保只初始化一次 return <div>消息组件</div>; }; export default MessageComponent;
3. 确认Heroku环境变量
确保Heroku后台已正确设置CLIENT_URL环境变量,值为你的生产环境客户端域名(如https://www.example.com)。
内容的提问来源于stack exchange,提问作者gfxmoda
相关产品推荐
相关产品推荐

