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

React+Node实时聊天应用中Socket.io出现Insufficient resources错误排查

解决Socket.io实时聊天应用的"Insufficient resources"连接错误

一、客户端核心问题:重复创建Socket连接与重复监听事件

你的客户端代码中,const socket = io("http://localhost:5000");直接写在组件函数内部,每次组件重新渲染(比如conv更新时)都会创建一个新的Socket连接,同时useEffect的依赖是conv,每次conv变化都会重新绑定socket.on("output")事件,导致:

  • 短时间内创建大量WebSocket连接,耗尽浏览器资源,触发"Insufficient resources"错误
  • 重复监听事件,导致消息重复接收或逻辑混乱

客户端修复代码

import React from "react";
import { useState, useEffect, useRef } from "react";
import { io } from "socket.io-client";

const App = () => {
  const [conv, setConv] = useState([]);
  const [input, setInput] = useState({
    sender: "user",
    text: "",
  });
  // 使用useRef保存Socket实例,避免重复创建
  const socketRef = useRef(null);

  useEffect(() => {
    // 只在组件挂载时创建一次Socket连接
    socketRef.current = io("http://localhost:5000");
    
    // 绑定output事件监听,仅执行一次
    const handleOutput = (data) => {
      setConv(data[0].messages);
    };
    socketRef.current.on("output", handleOutput);

    // 组件卸载时清理连接与监听
    return () => {
      socketRef.current.off("output", handleOutput);
      socketRef.current.disconnect();
    };
  }, []); // 空依赖,仅在组件挂载/卸载时执行

  const handleChange = (e) => {
    setInput({
      sender: "user",
      text: e.target.value,
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // 使用ref中的Socket实例发送事件
    socketRef.current.emit("input", input);
    // 清空输入框
    setInput(prev => ({...prev, text: ""}));
  };

  return (
    <div className="App">
      <form onSubmit={handleSubmit}>
        <h1>chat</h1>
        <div id="messages">
          {conv.map((msg, index) => (
            <div key={index}>
              <p>
                {msg.sender}: {msg.text}
              </p>
            </div>
          ))}
        </div>
        <input onChange={handleChange} type="text" value={input.text} />
        <input type="submit" />
      </form>
    </div>
  );
};
export default App;

修复点说明:

  • 用useRef存储Socket实例,确保组件生命周期内仅创建一次连接
  • 将Socket初始化与事件监听放在空依赖的useEffect中,避免重复执行
  • 组件卸载时清理事件监听并断开连接,防止内存泄漏
  • 给输入框添加value={input.text},确保受控组件逻辑正确

二、服务端潜在问题:数据更新后未同步客户端+空数据判断缺失

  1. 数据库更新后未通知客户端:当前服务端在updateOne后没有主动向客户端发送最新的聊天记录,导致客户端无法实时看到新消息
  2. 空数组访问风险:chat.find({ members: { $all: [sender, receiver] } }).toArray返回的res可能为空,此时访问res[0].members会抛出错误,导致服务端异常,影响Socket连接稳定性

服务端修复代码

const express = require("express");
const socketIo = require("socket.io");
const http = require("http");
const PORT = 5000;
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"] // 明确允许的方法,增强安全性
  },
}); 
const MongoClient = require("mongodb").MongoClient;

MongoClient.connect("mongodb://localhost:27017", { useUnifiedTopology: true }, (err, client) => {
  if (err) throw err;
  console.log("connected to MongoDB");

  const database = client.db("local");
  const chat = database.collection("chat");

  io.on("connection", (socket) => {
    // 推送初始聊天记录
    const sendInitialMessages = async () => {
      try {
        const res = await chat.find().toArray();
        socket.emit("output", res);
      } catch (err) {
        console.error("获取初始消息失败:", err);
      }
    };
    sendInitialMessages();

    // 处理输入事件
    socket.on("input", async (input) => {
      const { sender, text } = input;
      const receiver = "bot";

      try {
        // 查找对应聊天记录,添加空判断
        const res = await chat.find({ members: { $all: [sender, receiver] } }).toArray();
        if (res.length === 0) {
          console.log("未找到对应聊天记录");
          return;
        }

        const senderInDb = res[0].members[0];
        const receiverInDb = res[0].members[1];

        if (sender === senderInDb && receiver === receiverInDb) {
          // 更新数据库
          await chat.updateOne(
            { members: { $all: [sender, receiver] } },
            { $push: { messages: { sender, text } } }
          );
          // 更新完成后,重新查询并推送最新消息给所有连接的客户端
          const updatedRes = await chat.find().toArray();
          io.emit("output", updatedRes);
        }
      } catch (err) {
        console.error("处理输入消息失败:", err);
      }
    });

    // 客户端断开连接时的清理
    socket.on("disconnect", () => {
      console.log("客户端断开连接:", socket.id);
    });
  });
});

server.listen(PORT, (err) => {
  if (err) console.log(err);
  console.log("Server running on Port ", PORT);
});

修复点说明:

  • 改用async/await替代回调,代码更清晰且更易处理错误
  • 添加useUnifiedTopology: true参数,适配MongoDB新的连接拓扑
  • 在查询聊天记录后添加空数组判断,避免res[0]报错
  • 数据库更新完成后,用io.emit推送最新消息给所有客户端,实现实时同步
  • 添加错误捕获,避免单个请求失败导致服务端崩溃

三、其他注意事项

  • 确保MongoDB服务正常运行,且local数据库下的chat集合存在,且包含members和messages字段的文档
  • 浏览器控制台查看是否有其他错误,比如CORS问题(服务端已配置CORS,基本可排除)
  • 测试时关闭其他占用大量资源的应用,避免真的因系统资源不足导致连接失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:30:50