React Socket.IO客户端连接FastAPI服务后立即断开求助
Socket.IO连接后立即断开问题排查与解决
问题描述
FastAPI搭建的Python Socket服务,React客户端用socket.io-client连接时,Socket创建成功后立刻断开。已尝试手动调用connect()、确认客户端与服务端Socket.IO版本兼容,问题仍未解决。
服务端代码
Socket连接实现
import socketio sio = socketio.AsyncServer(cors_allowed_origins="*", async_mode="asgi") # define an asgi app socketio_app = socketio.ASGIApp(sio, socketio_path="/") @sio.event async def connect(sid, environ): print("connect ", sid) player_id = environ.get("HTTP_PLAYER_ID") game_id = environ.get("HTTP_GAME_ID") # if the parameters are not present, the connection is rejected if not player_id or not game_id: return False await s io.save_session(sid, {"player_id": player_id, "game_id": game_id}) sio.enter_room(sid, "g" + game_id) sio.enter_room(sid, "p" + player_id) print("connect ", sid, "player_id ", player_id, "game_id ", game_id)
服务端挂载配置
app.mount("/socket.io", socketio_app) origins = ["*"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
客户端代码
创建Socket实例
import { io } from "socket.io-client" // hardcoded values for headers const gameId = 1; const playerId = 1; const createSocket = (uri) => { const socket = io(uri, { extraHeaders: { game_id: gameId, player_id: playerId }, transports: ["websocket"], autoConnect: true, // Listen for events socket.on("connection", () => console.log("websocket connected")); return socket; } const gameSocket = createSocket("http://localhost:8000/") export {gameSocket};
Socket自定义Hook
import { gameSocket } from "./createSocket" import { useState, useEffect } from "react" const useSocket = () => { const [connected, setConnected] = useState(true); const handleConnChange = () => { setConnected(gameSocket.connected) } useEffect(() => { gameSocket.on("connect", handleConnChange); gameSocket.on("disconnect", handleConnChange); return () => { gameSocket.removeListener("connect", handleConnChange); gameSocket.removeListener("disconnect", handleConnChange); } }) return { gameSocket: gameSocket, isConnected: connected } } export default useSocket;
组件中使用Hook
import { useEffect, useRef, useState } from "react" import useSocket from "./useSocket"; const GameRouter = () => { const [gameState, setGameState] = useState(-1); const [gameData, setGameData] = useState({}); const [playerData, setPlayerData] = useState({}); const { gameSocket } = useSocket(); useEffect(() => { gameSocket.on("game_status", (data) => { setGameData(data); setGameState(data.state) }) }, [gameData]) return { <h1>websockets test</h1> } } export default GameRouter;
服务端运行日志
INFO: connection open connect UrwFItMNGGWN4FLaAAAT INFO: connection closed
问题排查与修复方案
1. 服务端语法错误(核心问题)
服务端connect事件处理函数存在两处致命错误:
await s与io.save_session被错误拆分,正确写法应为await sio.save_sessionio.save_session及后续代码缩进错误,未包含在if判断通过的代码块内sio.enter_room是异步方法,需添加await关键字
修复后的服务端connect函数:
@sio.event async def connect(sid, environ): print("connect ", sid) player_id = environ.get("HTTP_PLAYER_ID") game_id = environ.get("HTTP_GAME_ID") # if the parameters are not present, the connection is rejected if not player_id or not game_id: return False await sio.save_session(sid, {"player_id": player_id, "game_id": game_id}) await sio.enter_room(sid, "g" + game_id) await sio.enter_room(sid, "p" + player_id) print("connect ", sid, "player_id ", player_id, "game_id ", game_id)
2. 客户端事件监听错误
Socket.IO客户端的连接成功事件为connect,而非connection,修正监听代码:
// 原代码 socket.on("connection", () => console.log("websocket connected")); // 修正后 socket.on("connect", () => console.log("websocket connected"));
3. Socket路径配置匹配
服务端将socketio_app挂载到/socket.io路径,但创建时设置了socketio_path="/",导致路径不匹配。建议使用默认socketio_path(默认值为/socket.io):
# 原代码 socketio_app = socketio.ASGIApp(sio, socketio_path="/") # 修正后(移除自定义路径,使用默认值) socketio_app = socketio.ASGIApp(sio)
客户端连接地址保持http://localhost:8000/即可,socket.io-client会自动追加/socket.io路径。
4. 组件返回值语法错误
GameRouter组件的return语句错误使用大括号,需改为小括号:
// 原代码 return { <h1>websockets test</h1> } // 修正后 return ( <h1>websockets test</h1> )
5. Hook依赖项缺失
useSocket中的useEffect未设置依赖项,会导致重复绑定事件,添加空数组依赖确保仅挂载时执行一次:
useEffect(() => { gameSocket.on("connect", handleConnChange); gameSocket.on("disconnect", handleConnChange); return () => { gameSocket.removeListener("connect", handleConnChange); gameSocket.removeListener("disconnect", handleConnChange); } }, []) // 添加空数组依赖
验证修复
完成上述修改后,重启服务端与客户端,服务端日志应显示完整连接信息且不会立即断开:
INFO: connection open connect UrwFItMNGGWN4FLaAAAT connect UrwFItMNGGWN4FLaAAAT player_id 1 game_id 1
客户端控制台会输出websocket connected,表示连接成功。
内容的提问来源于stack exchange,提问作者Augusto Lallana
相关产品推荐
相关产品推荐

