question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Detect a "disconnected" client?

See original GitHub issue

Hi there. I am wondering if there a way to detect a disconnected client, and somehow kick him from the server? I have a problem if some client disconnect randomly(either by problem at their device or something else), than when i run a check at my server i can see multiple of same type of clients connected!

Is there any way to detect which client are not connected and kick them from the server somehow?

Here is an example of multiple of same clients connected:

ERROR::

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 153, in run_asgi
    result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/fastapi/applications.py", line 149, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/applications.py", line 102, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 146, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/exceptions.py", line 58, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 550, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 283, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 57, in app
    await func(session)
  File "/usr/local/lib/python3.8/site-packages/fastapi/routing.py", line 242, in app
    await dependant.call(**values)
  File "./main.py", line 83, in websocket_endpoint
    await connection_manager.send_JSON_object_privately_from_object_to_user()
  File "./websocket_manager.py", line 285, in send_JSON_object_privately_from_object_to_user
    await asyncio.gather(*(conn.ws.send_text(json.dumps(generate_new_JSON)) for conn in self.active_user_client_connections if conn.id == the_user_id))
  File "/usr/local/lib/python3.8/site-packages/starlette/websockets.py", line 128, in send_text
    await self.send({"type": "websocket.send", "text": data})
  File "/usr/local/lib/python3.8/site-packages/starlette/websockets.py", line 68, in send
    await self._send(message)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 224, in asgi_send
    raise RuntimeError(msg % message_type)
RuntimeError: Unexpected ASGI message 'websocket.send', after sending 'websocket.close'.
________________

TERMINAL INFO:

There are currently 0 user clients connected to WEBPAGE
There are currently 4 user clients connected through IPHONE or ANDROID
THE USER CLIENTS :: ['9', '9', '9', '9']

DEVICES:

There are currently 30 Woit-track`s connected to server
WOIT TRACKS CONNECTED: ['DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003', 'DEV003']

I forgot to mention, at some point(maybe after 10 minutes), they slowly gets removed by the list one by one… But i would love to detect this faster…

Here is my code:

#websocket_manager.py
class UserClientWebSocket(BaseModel):
    id: str
    ws: WebSocket

    class Config:
        arbitrary_types_allowed = True


class WoitTrackClientWebSocket(BaseModel):
    id: str
    ws: WebSocket

    class Config:
        arbitrary_types_allowed = True

class WoitSenseClientSocket(BaseModel):
    id: str
    ws: WebSocket

    class Config:
        arbitrary_types_allowed = True


class WebPageClientWebSocket(BaseModel):
    id: str
    ws: WebSocket

    class Config:
        arbitrary_types_allowed = True


class ConnectionManager:
    def __init__(self):

        self.active_user_client_connections: List[UserClientWebSocket] = []
        self.collect_user_IDs = []

        self.active_webpage_client_connections: List[WebPageClientWebSocket] = []
        self.collect_webpages_IDs = []

        self.active_woit_track_connections: List[WoitTrackClientWebSocket] = []
        self.collect_woit_track_IDs = []

        self.active_woit_sense_connections: List[WoitSenseClientSocket] = []
        self.collect_woit_sense_IDs = []

    async def connect_the_user_client(self, websocket: WebSocket, THE_USER_ID):
        await websocket.accept()

        if THE_USER_ID not in self.collect_user_IDs:
            self.collect_user_IDs.append(THE_USER_ID)
        else:
    
            #await self.broadcast_message_to_both_user_and_webpage_clients(f"Somebody connected with the same ID as client: {THE_USER_ID}")
            #await self.broadcast_message_to_both_user_and_webpage_clients("but Vlori is a nice and kind guy, so he wil not get kicked :)")
            self.collect_user_IDs.append(THE_USER_ID)
        self.active_user_client_connections.append(UserClientWebSocket(ws=websocket, id=THE_USER_ID))
        await self.send_welcome_message_to_user(f"Dear: {THE_USER_ID}, You have connected to the server, now you just have to subscribe!", THE_USER_ID)

        generate_json = {"userID": THE_USER_ID, "subscribe" : 1}
        await self.send_welcome_message_to_user(json.dumps(generate_json), THE_USER_ID)

        await self.show_number_of_clients()


    async def connect_woit_track_client(self, websocket: WebSocket, THE_SENSOR_ID):
        await websocket.accept()
        if THE_SENSOR_ID not in self.collect_woit_track_IDs:
            self.collect_woit_track_IDs.append(THE_SENSOR_ID)
        else:
            #self.collect_woit_track_IDs.remove(THE_SENSOR_ID)
            await self.broadcast_message_to_webpage_clients(f"Somebody connected with the same ID as client: {THE_SENSOR_ID}")
            await self.broadcast_message_to_webpage_clients("but Vlori is a nice and kind guy, so he wil not get kicked :)")


            self.collect_woit_track_IDs.append(THE_SENSOR_ID)
        self.active_woit_track_connections.append(WoitTrackClientWebSocket(ws=websocket, id=THE_SENSOR_ID))
        await self.show_number_of_clients()

#main.py:

@app.websocket("/ws/woittrack/{woit_track_id}")
async def websocket_endpoint(websocket: WebSocket, woit_track_id: str):
    await connection_manager.connect_woit_track_client(websocket, woit_track_id)
    try:
        await connection_manager.show_number_of_clients()
        try:
            while True:
                data = await websocket.receive_text()

                await receive_JSON_data_from_sensor(data)

                await connection_manager.broadcast_message_to_webpage_clients(data)

                await connection_manager.broadcast_message_to_webpage_clients("")
                await connection_manager.send_JSON_object_privately_from_object_to_user()


        except json.JSONDecodeError:
            await connection_manager.send_personal_message_to_user("You have not sent JSON object correctly.. fix it plz", websocket)
            print(f"Woit-track #{woit_track_id} tries to send JSON object but is not sending it correctly!")

    except WebSocketDisconnect as e:
        print(f"Woit-track: #{woit_track_id} left the chat", e)
        await connection_manager.disconnect_the_woit_track_client(websocket, woit_track_id)
        await connection_manager.broadcast_message_to_both_user_and_webpage_clients(f"Woit-track: #{woit_track_id} left the chat")

@app.websocket("/ws/user/{user_id}")
async def websocket_endpoint(websocket: WebSocket, user_id: str):
    await connection_manager.connect_the_user_client(websocket, user_id)
    await resume_streaming_for_user(user_id)
    try:
        await connection_manager.show_number_of_clients()

        while True:
            try:
                data = await websocket.receive_json()


                userID_from_json = data["userID"]
                if userID_from_json == user_id:

                    await multithread_this_shit(data)
                else:
                    await websocket.send_text(f"_________________________________________________________________________________")
                    await websocket.send_text(f"                        userID in json-object is wrong!")
                    await websocket.send_text(f"the userID inside json object must be the same as the one you are connected with!")
                    await websocket.send_text(f"_________________________________________________________________________________")


            except json.JSONDecodeError as e:
                #await connection_manager.send_personal_message_to_user( "You have not sent JSON object correctly.. fix it plz", websocket)
                print(f"User #{user_id} tries to send JSON object but is not sending it correctly!")
                print(e)

    except WebSocketDisconnect as e:
        print(f"User-client #{user_id} left the chat", e)
        #await remove_user_algorithm_object_from_dictionary(user_id)
        await connection_manager.disconnect_the_user_client(websocket, user_id)
        await pause_streaming_for_user(user_id)
        #await delete_both_user_and_sleep_call_to_sensor_at_once(user_id)

        #await connection_manager.broadcast_message_to_both_user_and_webpage_clients(f"User #{user_id} left the chat")

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
includeamincommented, Dec 10, 2020

can I kick the already connected client from the server and allow the new one to connect

check this code. I’ve changed the Connection Manager Class. I hope it helps you.

from typing import List, Dict

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse

app = FastAPI()
html = """
<!DOCTYPE html>
<html>
    <head>
        <title>Chat</title>
    </head>
    <body>
        <h1>WebSocket Chat</h1>
        <h2>Your ID: <span id="ws-id"></span></h2>
        <form action="" onsubmit="sendMessage(event)">
            <input type="text" id="messageText" autocomplete="off"/>
            <button>Send</button>
        </form>
        <ul id='messages'>
        </ul>
        <script>
            var client_id = 444
            document.querySelector("#ws-id").textContent = client_id;
         
            var ws = new WebSocket(`ws://localhost:8000/ws/${client_id}`);
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById("messageText")
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>
"""


class ConnectionManager:
    def __init__(self):
        self.active_connections: Dict[str, WebSocket] = {}

    async def connect(self, websocket: WebSocket, client_id: str):
        await websocket.accept()
        exist: WebSocket = self.active_connections.get(client_id)
        if exist:
            await exist.close()  # you want to disconnect connected client.
            self.active_connections[client_id] = websocket
            # await websocket.close()  # reject new user with the same ID already exist
        else:
            self.active_connections[client_id] = websocket

    def disconnect(self, websocket: WebSocket, client_id: str):
        self.active_connections.pop(client_id)

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

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


manager = ConnectionManager()


@app.get("/")
async def get():
    return HTMLResponse(html)


@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
    await manager.connect(websocket, client_id)
    try:
        while True:
            data = await websocket.receive_text()
            await manager.send_personal_message(f"You wrote: {data}", websocket)
            await manager.broadcast(f"Client #{client_id} says: {data}")
    except WebSocketDisconnect:
        manager.disconnect(websocket, client_id)
        await manager.broadcast(f"Client #{client_id} left the chat")

2reactions
ghostcommented, Apr 13, 2021

This is coooooooooooooooool! THank you very much

Read more comments on GitHub >

github_iconTop Results From Across the Web

Instantly detect client disconnection from server socket
This way, I'm able to safely detect disconnection between TCP ... This is only possible if both client and server are under your...
Read more >
How to detect closed TCP connection when client ...
These looks promising, answer 3 in .net - Instantly detect client disconnection from server socket - Stack Overflow[^]
Read more >
Detecting client disconnections in the server side
You can't really tell if the client is accidentally disconnected, but you can find out with a few seconds by turning on keep-alives....
Read more >
How am I supposed to detect client's socket disconnection in ...
The only way to reliably test for disconnection is to have the client periodically send data across the connection. This is called a...
Read more >
TCPListener how to detect client socket disconnected - MSDN
So, essentially, you're not really asking the right question. The only way that you know a client has disconnected is that it stops...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found