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

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_session
  • io.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:57:19