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

React+Flask(Vite)集成Socket.IO遇连接循环与断开问题求助

React+Flask集成Socket.IO异常排查

问题描述

在React+Flask应用中集成Socket.IO时遇到两个异常:

  • 启动后端服务器后,持续触发用户连接的循环日志;
  • 启动前端后,用户连接频繁断开。

基于教程搭建,仅用Vite替代Create React App,未发现代码明显差异,需排查原因。


环境与代码

Python依赖包

bidict==0.22.1
blinker==1.6.2
click==8.1.6
dnspython==2.4.1
Flask==2.3.2
Flask-Cors==4.0.0
Flask-SocketIO==5.3.5
greenlet==2.0.2
gunicorn==21.2.0
h11==0.14.0
importlib-metadata==6.8.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
packaging==23.1
pyserial==3.5
python-dotenv==1.0.0
python-engineio==4.5.1
python-socketio==5.8.0
simple-websocket==0.10.1
six==1.16.0
Werkzeug==2.3.6
wsproto==1.2.0
zipp==3.16.2

后端代码(app.py)

from flask import Flask, request
from flask_cors import CORS
from flask_socketio import SocketIO, emit


app = Flask(__name__)
CORS(app, resources={r"*": {"origins": "*"}})
socketio = SocketIO(app, cors_allowed_origins='*')


@socketio.on("connect")
def connected():
    """event listener when client connects to the server"""
    print('-' * 25)
    print(f"Client has connected: {request.sid}")
    emit("connect", {"data": f"id: {request.sid} is connected"})
    print('-' * 25)


@socketio.on("disconnect")
def disconnected():
    """event listener when client disconnects to the server"""
    print('-' * 25)
    print("User disconnected")
    emit("disconnect", f"User {request.sid} disconnected", broadcast=True)
    print('-' * 25)


if __name__ == "__main__":
    socketio.run(app, debug=True)

前端依赖配置(package.json)

{
  "name": "socket-example",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@reduxjs/toolkit": "^1.9.5",
    "@types/react-redux": "^7.1.25",
    "framer-motion": "^10.12.22",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-hot-toast": "^2.4.1",
    "react-icons": "^4.10.1",
    "react-redux": "^8.1.1",
    "socket.io": "^4.7.1",
    "socket.io-client": "^4.7.1",
    "uuid": "^9.0.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.14",
    "@types/react-dom": "^18.2.6",
    "@types/uuid": "^9.0.2",
    "@typescript-eslint/eslint-plugin": "^5.61.0",
    "@typescript-eslint/parser": "^5.61.0",
    "@vitejs/plugin-react": "^4.0.1",
    "autoprefixer": "^10.4.14",
    "eslint": "^8.44.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.1",
    "postcss": "^8.4.26",
    "prettier": "3.0.0",
    "tailwindcss": "^3.3.3",
    "typescript": "^5.0.2",
    "vite": "^4.4.0"
  }
}

前端组件代码(App.tsx)

import { useState, useEffect } from 'react'
import { io } from 'socket.io-client'

const URL = import.meta.env.MODE === 'production' ? undefined : 'http://localhost:5000'

const socket = io(URL, {
  cors: {
    origin: 'http://localhost:5173',
  },
})

export default function App() {
  const [isConnected, setIsConnected] = useState(socket.connected)

  useEffect(() => {
    socket.on('connect', (data) => {
      console.log(data)
      setIsConnected(true)
    })

    socket.on('disconnect', (data) => {
      console.log(data)
      setIsConnected(false)
    })

    return () => {
      socket.disconnect()
    }
  }, [])

  return (
    <>
      <p>WebSocket: {'' + isConnected.toString()}</p>
    </>
  )
}

问题原因分析

  1. 内置事件冲突导致循环连接:后端在connect事件回调中,emit了名为connect的事件——这是Socket.IO的内置事件。客户端监听该内置事件时,会触发连接逻辑,导致后端不断收到新的连接请求,形成循环日志。

  2. 前端错误的连接清理逻辑:前端useEffect的返回函数中调用socket.disconnect(),在React严格模式下(Vite默认开启),组件会被挂载两次,导致第一次挂载后断开连接,第二次挂载重新连接,表现为连接频繁断开。同时,connect和disconnect内置事件本身没有参数,前端接收data属于错误用法。


解决方案

1. 修改后端事件名称

将后端emit的内置事件名改为自定义名称,避免冲突:

@socketio.on("connect")
def connected():
    """event listener when client connects to the server"""
    print('-' * 25)
    print(f"Client has connected: {request.sid}")
    # 替换为自定义事件名
    emit("client-connected", {"data": f"id: {request.sid} is connected"})
    print('-' * 25)

2. 修正前端监听与清理逻辑

  • 移除不必要的socket.disconnect(),Socket.IO客户端会自动管理连接生命周期;
  • 修正内置事件的监听逻辑(无参数);
  • 监听后端自定义的连接事件;
  • 简化CORS配置(后端已允许所有来源):
import { useState, useEffect } from 'react'
import { io } from 'socket.io-client'

const URL = import.meta.env.MODE === 'production' ? undefined : 'http://localhost:5000'

// 简化CORS配置
const socket = io(URL)

export default function App() {
  const [isConnected, setIsConnected] = useState(socket.connected)

  useEffect(() => {
    // connect内置事件无参数
    socket.on('connect', () => {
      console.log('已连接到服务器')
      setIsConnected(true)
    })

    // disconnect内置事件无参数
    socket.on('disconnect', () => {
      console.log('已断开与服务器的连接')
      setIsConnected(false)
    })

    // 监听后端自定义的连接通知事件
    socket.on('client-connected', (data) => {
      console.log(data)
    })

    // 组件卸载时移除事件监听,避免内存泄漏
    return () => {
      socket.off('connect')
      socket.off('disconnect')
      socket.off('client-connected')
    }
  }, [])

  return (
    <>
      <p>WebSocket连接状态:{isConnected ? '已连接' : '未连接'}</p>
    </>
  )
}

3. 版本兼容性确认

当前后端Flask-SocketIO==5.3.5与前端socket.io-client==4.7.1协议版本兼容(均为Socket.IO v4.x),无需调整版本。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 15:54:51