Flutter关闭WebSocket后FastAPI端未关闭且无异常问题排查
我正在集成FastAPI的WebSocket端点和Flutter应用,建立连接后通过前端按钮关闭Flutter侧的WebSocket连接,预期FastAPI侧的连接也会随之关闭并触发WebSocketDisconnectException,但实际情况是FastAPI侧连接未关闭,也没触发异常——哪怕Flutter已经显示连接关闭,FastAPI的WebSocketclient_state仍保持CONNECTED状态。
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; } }
@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侧也能同步关闭,方便后续重新连接或执行其他操作。
尝试在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

