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> </> ) }
问题原因分析
内置事件冲突导致循环连接:后端在
connect事件回调中,emit了名为connect的事件——这是Socket.IO的内置事件。客户端监听该内置事件时,会触发连接逻辑,导致后端不断收到新的连接请求,形成循环日志。前端错误的连接清理逻辑:前端
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
相关产品推荐
相关产品推荐

