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

Socket.IO连接被拒绝(ERR_CONNECTION_REFUSED)问题排查求助

聊天应用Socket.IO连接ERR_CONNECTION_REFUSED问题排查方案

问题背景

使用Socket.IO和React开发聊天应用时,持续遭遇ERR_CONNECTION_REFUSED错误,无法连接服务器。已尝试的操作包括:

  • 在CORS ORIGIN中配置不同IP(含['*'])
  • 前端尝试多种连接地址:io('localhost:4444')、io('配置了反向代理到4444端口的域名')
  • 仅通过虚拟机的数值IP(io('195.XXX.XX4:4444'))能成功连接

环境信息:前端部署在3000端口,Socket服务计划运行在4444端口。

相关代码片段

React前端代码

let newSocket;

try {
  newSocket = io.connect('http://localhost:4444');
} catch (e) {
  console.log(e);
  window.location.reload();
}

setSocket(newSocket);

return () => {
  newSocket.disconnect();
};

Socket.IO服务端代码(注释为葡萄牙语)

const express = require('express');
const mongoose = require('mongoose');
const http = require('http');
const socketIo = require("socket.io");
const Chat = require('./models/Chat.js');
const dotenv = require("dotenv");
const cors = require('cors'); // Importe a biblioteca CORS

const app = express();
app.use((req, res, next) => {
    res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' https://www.googletagmanager.com https://cdnjs.cloudflare.com 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com;");
    next();
});

dotenv.config();

const port = process.env.PORT || 3000;
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: ['http://localhost:3000'],
    methods: ['GET', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
  },
  debug: true,
});


// Use a biblioteca CORS como middleware
app.use(cors({
  origin: ['http://localhost:3000'],
  methods: ['GET', 'POST', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
}));

mongoose.connect(`mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@yc-cluster.bn5qm0z.mongodb.net/socketio`, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
})
.then(() => {
  console.log('Connected to MongoDB Atlas');
})
.catch((err) => {
  console.error('Error connecting to MongoDB Atlas:', err);
});

const chatRoutes = require('./routes/chatRoutes');
app.use(express.json());
app.use('/api', chatRoutes);

// SOCKET DE CONEXÃO


io.on('connection', (socket) => {
  socket.on("connect_error", (err) => {
    console.log(`connect_error due to ${err.message}`);
  });
  console.log('User connected in socket');
  socket.on('join chat', (id_do_clube) => {
    const { id_club } = id_do_clube;
    const chatRoom = 'chat_' + id_club; 
    socket.join(chatRoom);
    console.log('User connected in chat room: ' + 'chat_' + id_club);

    socket.on('disconnect', () => {
      console.log('User disconnected');
    });

    // SOCKET DE ENVIO DE MENSAGENS
    socket.on('chat message', async (message) => {
      
      // Salvar a mensagem no banco de dados
      const chat = new Chat({
        sender: message.sender,
        message: message.message,
        id_club: message.id_club,
        id_user: message.id_user,
        sender: message.sender,
        sender_image: message.sender_image,
        announcement: message.announcement,
        type: message.type,
        role: message.role,
      });
      try {
        const newChat = await chat.save();
        socket.emit('chat message', newChat);
        io.to(chatRoom).emit('chat message', newChat);
        socket.emit('chat message response', "Mensagem enviada com sucesso");
      } catch (err) {
        console.error('Error saving chat message:', err);
      }
    });

    socket.on('get old messages', async (id_club) => {
      try {
        const oldChats = await Chat.find({ id_club }).sort({ timestamp: 'asc' });
        socket.emit('old messages', oldChats);
      } catch (err) {
        console.error('Error retrieving old messages:', err);
      }
    });

    // SOCKET DE EDIÇÃO DE MENSAGENS
    socket.on('edit message', async (editedMessage) => {
      try {
        const { messageId, newMessage, id_user } = editedMessage;
        const chat = await Chat.findById(messageId);
    
        if (!chat) {
          console.error('Mensagem não encontrada');
        }
    
        if (parseInt(chat.id_user) !== parseInt(id_user)) {
          console.error('Usuário sem permissão de editar a mensagem');
        }
    
        const updatedChat = await Chat.findByIdAndUpdate(
          messageId,
          { message: newMessage, edited: true },
          { new: true }
        );
    
        // Emitir evento para informar que deu certo para o cliente
        socket.emit('message edited response', "Mensagem editada com sucesso");
        
        // Obter as mensagens atualizadas para o chat específico
        const id_club = updatedChat.id_club;
        const updatedMessages = await Chat.find({ id_club }).sort({ timestamp: 'asc' });

        // Emitir evento para atualizar a lista de mensagens para o chat específico
        io.to(chatRoom).emit('updated message list', { id_club, messages: updatedMessages });
        
      } catch (err) {
        console.error('Error editing message:', err);
      }
    });
    
    // SOCKET DE DELEÇÃO DE MENSAGENS
    socket.on('delete message', async (infos) => {
      try {
        const { messageId, id_user } = infos;
        const chat = await Chat.findById(messageId);
        if (!chat) {
          console.error('Mensagem não encontrada');
          return;
        }

        // Verificar se o usuário que está tentando deletar a mensagem é o mesmo que a enviou
        if (parseInt(chat.id_user) !== parseInt(id_user)) {
          console.error('Usuário sem permissão de deletar a mensagem');
          return;
        }
        
        // Salvar a mensagem antiga antes de excluí-la
        const oldMessage = chat.message;

        // Atualizar a mensagem para "Esta mensagem foi excluída" e marcar como deletada
        chat.message = "Esta mensagem foi excluída";
        chat.deleted = true;
        chat.oldMessage = oldMessage; // Salvar a mensagem antiga em um campo separado, se necessário

        // Salvar as alterações no documento
        await chat.save();
            // Emitir evento para informar que deu certo a atualização da mensagem para o cliente
            socket.emit('message deleted response', "Mensagem excluída com sucesso");

        // Obter as mensagens atualizadas para o chat específico
        const id_club = chat.id_club;
        const updatedMessages = await Chat.find({ id_club }).sort({ timestamp: 'asc' });

        // Emitir evento para atualizar a lista de mensagens para o chat específico
        io.to(chatRoom).emit('updated message list', { id_club, messages: updatedMessages });
      } catch (err) {
        console.error('Error deleting message:', err);
      }
    });
  });
});
  
server.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

后续排查/解决方法

  • 修正服务端监听端口:代码中const port = process.env.PORT || 3000;与你所说的4444端口不符,若未配置PORT环境变量,服务实际运行在3000端口。要么在.env文件中设置PORT=4444,要么直接将代码改为const port = 4444;,确保前端连接端口与服务端监听端口一致。

  • 统一CORS配置:Socket.IO的cors配置与express的cors中间件需保持一致,若前端通过域名访问,需将该域名添加到origin列表,而非仅保留localhost:3000。同时,Socket.IO的cors配置中建议添加OPTIONS方法,以支持跨域预检请求。

  • 检查反向代理配置:若使用反向代理(如Nginx),需确保配置支持WebSocket协议。示例Nginx配置:

    location /socket.io {
        proxy_pass http://localhost:4444;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
    

    前端连接时直接使用域名(如io('https://你的域名')),无需添加端口,由反向代理处理转发。

  • 开放服务器端口权限:检查虚拟机防火墙、云服务商安全组,确保服务监听的端口(4444或实际端口)允许入站连接。仅数值IP能连接说明IP端口已开放,但域名解析后的端口可能未配置正确。

  • 强制前端使用WebSocket传输:前端连接时明确指定传输方式,避免轮询可能引发的问题:

    newSocket = io.connect('http://localhost:4444', { transports: ['websocket'] });
    

    同时确保协议(http/https)完整,不要省略。

  • 验证域名解析与本地hosts:若使用域名连接,检查域名DNS解析是否正确指向虚拟机IP,本地hosts文件是否存在错误的域名映射。

  • 让服务监听所有网络接口:修改服务启动代码,让服务监听0.0.0.0而非默认的localhost,确保外部请求能访问:

    server.listen(port, '0.0.0.0', () => {
      console.log(`Server is running on port ${port}`);
    });
    

内容的提问来源于stack exchange,提问作者Guidetti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:19:50