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

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

  });
}

问题原因

  1. Heroku端口限制:Heroku仅为每个应用分配一个动态端口,不允许开放多个独立端口。你的代码让Socket.io单独监听8900端口,这个端口在生产环境无法被外部访问,导致连接超时。
  2. Socket.io与Express服务器分离:当前代码将Socket.io和Express服务器分别启动在不同端口,不符合Heroku的部署规则。
  3. 客户端连接配置错误:生产环境使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:03:39