WebSockets in FastAPI – ConnectionClosedOK: received 1000 (OK)

Question:

I have 3 clients, who periodically send data to my server. I use the example from FastAPI’s documentation.

This is my server code:

class ConnectionManager:
    def __init__(self):
        self.active_connections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def send_response(self, message: dict, websocket: WebSocket):
        await websocket.send_json(message)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)

manager = ConnectionManager()

@app.websocket("/chargeStationState/{client_id}")
async def websocket_endpoint(websocket: WebSocket,
                             client_id: int,
                             db: Session = Depends(deps.get_db)):

    await manager.connect(websocket)
    try:
        while True:
            message = await websocket.receive_json()
            logging.info(message)
          
            ## read data from db
            response = {
                             "stations": "repsonse",
                             "timestamp": int(time.time())
            }
            await manager.send_response(response, websocket)
            #await manager.broadcast(f"Client #{client_id} says: {data}")

    except WebSocketDisconnect:
        manager.disconnect(websocket)

This is the client code:

async for websocket in websockets.connect("ws://127.0.0.1:8001/chargeStationState/1"):
            message = {'name':'station1'}
            await websocket.send(json.dumps(message))
            p = await asyncio.wait_for(websocket.recv(), timeout=10)
            print(p)
            await asyncio.sleep(2)

So, I would like to have 5 clients, who will talk to my server and send sensor data, but after 5 minutes I get the following error

websockets.exceptions.ConnectionClosedOK: received 1000 (OK) 

then receiving 1000 (OK) and cannot identify where the issue lies.

Asked By: Matej Senožetnik

||

Answers:

Testing the code you provided, it doesn’t seem that one could reproduce the problem you are referring to. Hence, the problem likely lies elsewhere in your code. A working example is provided below, based on the code provided in your question. Related examples can be found here, as well as here and here.

Working Example

app.py

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import uvicorn
import time

class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def send_personal_message(self, message: str, websocket: WebSocket):
        await websocket.send_json(message)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_json(message)


app = FastAPI()
manager = ConnectionManager()

@app.websocket('/chargeStationState')
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            data = await websocket.receive_json()
            response = {'station': data['station'], 'timestamp': time.ctime()}
            await manager.send_personal_message(response, websocket)
    except WebSocketDisconnect:
        manager.disconnect(websocket)

test.py

import websockets
import asyncio
import json

async def main():
    url = 'ws://127.0.0.1:8000/chargeStationState'
    data = json.dumps({'station':'1'})
    
    async for websocket in websockets.connect(url):
        await websocket.send(data)
        print(await asyncio.wait_for(websocket.recv(), timeout=10))
        await asyncio.sleep(2)
        
asyncio.run(main())

test.py (revised)

In the previous test.py code example, the websocket connection would be closed and a new one would be established every time data are sent to the server (using the async for expression). Unless that is what suits you best, the code example below takes care of that by using a while loop statement inside the async for loop. That means that the (same) connection established in the first place will remain open, unless an exception occurs in the body of the loop, which is handled in a way that connect() is reached, in order to reconnect with the next iteration (otherwise, you could let the exception bubble up and break out of the loop). If an error occurs while establishing the connection, connect() retries with exponential backoff. The backoff delay starts at three seconds and increases up to one minute (see the relevant documentation).

import websockets
import asyncio
import json

async def main():
    url = 'ws://127.0.0.1:8000/chargeStationState'
    data = json.dumps({'station':'1'})
    
    async for websocket in websockets.connect(url):
        try:
            while True:
                await websocket.send(data)
                print(await asyncio.wait_for(websocket.recv(), timeout=10))
                await asyncio.sleep(2)
        except websockets.ConnectionClosed:
            continue
        
asyncio.run(main())
Answered By: Chris