Search code examples
pythonflutterwebsocketfastapi

FastAPI Websocket not closing or raising exception after closing the associated Flutter Websocket


Overview

I'm integrating an endpoint from FastAPI with Flutter in which I establish a Websocket connection. Afterwards, the Flutter frontend closes the Websocket connection via a click on a button. What's supposed to happen is the Websocket connection from the FastAPI side to close as well, but the Websocket doesn't close and no WebSocketDisconnectException is raised, although the Flutter websocket is signalled as closed. Also, the websocket's client state remains as CONNECTED even though I closed it.

Flutter Websocket Code

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 Code

@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') # Suggested solution from Edit 1
            await asyncio.sleep(0.1) # Suggested solution from Edit 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")

I've tried receiving messages in the FastAPI websocket, and trying to catch the exception raised when trying to send a message through a websocket that is closed, but it doesn't seem to change the outcome. Not only does the websocket not close, I can't close the FastAPI server with 'CTRL+C'.

What I need is, after the Flutter websocket is closed, the FastAPI one be closed as well, so I can later reconnect if needed, or simply do other actions.

Edit 1

Tried adding a await asyncio.sleep(0.1) (and a print statement to know that it is running) to the while True loop, but the problem persists. I'll add below two screenshots showcasing the behaviour at the moment, with this solution.

The intended outcome is to, after closing the websocket from the Flutter side, as ilustrated by the first screenshot, the process to stop running as well.

Output before and after closing the socket from the Flutter side. Output before and after closing the sock

Output after using CTRL+C two times in a row. Output after using CTRL+C two times in a row.


Solution

  • Possible Working Solution!

    After some time digging through StackOverflow and Reddit, i've tried the following code within my while True loop:

    try:
        await asyncio.wait_for( websocket.receive_text(), timeout=0.1)
    except asyncio.TimeoutError:
        pass
    

    It seems to trigger a WebSocketDisconnect exception when the websocket is already closed. For anyone having a similar problem, try it out! I'm going to experiment a bit more with this, and if it does work, i'll close this issue.