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

Flutter关闭WebSocket后FastAPI端未关闭且无异常问题排查

概述

我正在集成FastAPI的WebSocket端点和Flutter应用,建立连接后通过前端按钮关闭Flutter侧的WebSocket连接,预期FastAPI侧的连接也会随之关闭并触发WebSocketDisconnectException,但实际情况是FastAPI侧连接未关闭,也没触发异常——哪怕Flutter已经显示连接关闭,FastAPI的WebSocketclient_state仍保持CONNECTED状态。

Flutter WebSocket代码
class RealtimeDataService {
  WebSocketChannel? channel;
  void start(StreamController<Map> controller, String cameraID) async {
    channel = WebSocketChannel.connect(
      Uri.parse('ws://localhost:8001/camera/$cameraID'),
    );
    channel!.stream.listen((event) async {
      Map data = bson.BsonCodec.deserialize(bson.BsonBinary.from(event));
      controller.add(data);
    });
  }

  void stop() {
    if (channel == null) return;
    channel!.sink.close();
    channel = null;
  }
}
FastAPI WebSocket代码
@app.websocket("/camera/{camera_id}")
async def read_camera(websocket: WebSocket, camera_id: int):
    await websocket.accept()

    try:
        while True:
            # print(websocket.client_state.name)
            print('Running') # 编辑1中尝试的解决方案
            await asyncio.sleep(0.1) # 编辑1中尝试的解决方案
    except WebSocketDisconnect:
        print('Client disconnected')
        raise HTTPException(status_code=200, detail="Client disconnected")
    except:
        print('Error capturing camera')
        await websocket.close()
        raise HTTPException(status_code=200, detail="Camera is not available")

我试过在FastAPI的WebSocket中接收消息,也试过捕获向已关闭连接发送消息的异常,但都没解决问题。现在不仅WebSocket关不掉,连按CTRL+C都无法关闭FastAPI服务器。

我的需求是:Flutter侧关闭WebSocket后,FastAPI侧也能同步关闭,方便后续重新连接或执行其他操作。

编辑1

尝试在while True循环中加入await asyncio.sleep(0.1)(同时加打印语句确认运行状态),但问题依旧。当前方案下的行为如下:

  • Flutter侧关闭前后的输出截图
  • 连续两次按CTRL+C后的输出截图

解决方法

问题根源

FastAPI的WebSocket依赖IO操作(读/写)来检测客户端连接状态,单纯的asyncio.sleep()只会让出事件循环时间,但不会触发WebSocket对连接断开的检测。另外,WebSocket路由中抛出HTTPException是错误用法——WebSocket协议和HTTP独立,应该通过关闭连接帧处理,而非HTTP异常。

方案1:定期触发连接检测

在循环中加入读取操作,哪怕不需要客户端发送消息,当客户端断开时,读取操作会直接抛出WebSocketDisconnect异常:

@app.websocket("/camera/{camera_id}")
async def read_camera(websocket: WebSocket, camera_id: int):
    await websocket.accept()

    try:
        while True:
            # 用超时读取检测连接状态,超时则继续循环
            await asyncio.wait_for(websocket.receive_text(), timeout=0.1)
            print('Running')
    except asyncio.TimeoutError:
        continue
    except WebSocketDisconnect:
        print('Client disconnected')
        await websocket.close()
    except Exception as e:
        print(f'Error capturing camera: {e}')
        await websocket.close()

方案2:启用自动Ping/Pong机制

FastAPI支持通过设置ping_interval和ping_timeout自动发送ping帧,客户端未按时返回pong则触发断开:

@app.websocket("/camera/{camera_id}")
async def read_camera(websocket: WebSocket, camera_id: int):
    # 每1秒发一次ping,3秒未收到pong则判定连接断开
    await websocket.accept(ping_interval=1, ping_timeout=3)

    try:
        while True:
            print('Running')
            await asyncio.sleep(0.1)
    except WebSocketDisconnect:
        print('Client disconnected')
    except Exception as e:
        print(f'Error capturing camera: {e}')
        await websocket.close()

修复服务器无法终止问题

原代码的except:捕获了所有异常,包括键盘中断(KeyboardInterrupt),导致CTRL+C无法退出。修改异常捕获逻辑:

except Exception as e:
    if isinstance(e, KeyboardInterrupt):
        raise
    print('Error capturing camera')
    await websocket.close()

Flutter侧优化

确保Flutter正确发送关闭帧,可添加监听确认关闭状态:

void stop() {
  if (channel == null) return;
  channel!.stream.listen(
    (_) {},
    onDone: () => print('Flutter WebSocket closed'),
    onError: (e) => print('Flutter WebSocket error: $e'),
  );
  channel!.sink.close();
  channel = null;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:49:50