Websocket handler detect disconnect

I’m new to the Sanic framework and I have a little problem with a websocket handler.
My code looks like that:

import asyncio
import sanic
from websockets.exceptions import ConnectionClosed

@app.exception(ConnectionClosed)
async def hey(req, ex):
    print("print this?")

@app.websocket('/random/<nickname:[A-z]+>')
async def random_ws_handler(request, ws, nickname):
    while True:
        if ws is None:
            print("closed")
            break
        else:
            print("connection is on")
        await asyncio.sleep(0.2)
    print("Handler shutting down")

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000, debug=True)

As you can see, Under my websocket handler random_ws_handler I run a while loop that prints “connection is on” all the time the connection is still running.
Then I try to print “closed” when the connection is closed by the client and finally - print “Handler shutting down”.

When I run the server and connect with a simple client. the meesage “connection is on” is written to the screen perfectly. But when I close the connection from the client, I don’t see any detection in the server, no “closed” or “Handler is shutting down”. It seems the handler just stop running (I no longer see the “connection is on” message)…
How can I detect when a handler finish his work (like when his websocket connection get closed)?
I tried to use @app.exception(ConnectionClosed) But it does not work… maybe i’m not using it in the right way?

The main reason for me to detect that is to announce to redis pubsub clients that the websocket left(user) and for close the connection itself to the redis server and stuff like that…
Any suggestions?

Thanks!

I can give you a snippet over the weekend when I am at a computer. In the mean time, take a look at this gist that I prepared to handle both websockets and pubsub. It might help push you in the right direction. It has handling for detecting when a connection is closed client side.

Hello @ahopkins
I have been troubled by this problem for a day. Seeing your reply here, my understanding is that the client disconnection needs to be actively detected in Sanic, and cannot be triggered passively, is it true? Looking forward to your reply

:unamused: Not sure I understand the question.

Should you add client disconnections logic?

Yes, if you need it.

Sorry, my English is so bad, you can see the description on the picture

@ahopkins
I may know the reason
After I changed to python3.7, it was no problem

2 Likes

Tried personally, Seems Python has change asyncio.CancelledError’s parent from Exception to BaseException 3.8 above.

Yes, Exceptions — Python 3.9.2 documentation.

This has come up a number of times before. We should add a note about this in the new docs “How to…” section when we get to adding the examples about websockets.

@wangjianweiwei @ahopkins
I have encountered the same problem in python3.6 last year.
I maintain a list in request.app to solved it, like this:

@word_bp.websocket('/words/push')
async def words_push(request, ws):
    while True:
        if ws not in request.app.ws_group:
            request.app.ws_group.append(ws)
        data = await ws.recv()
        for ws_part in request.app.ws_group:
            try:
                await ws_part.send(data)
            except Exception as e:
                request.app.ws_group.remove(ws_part)
                print_log("获取关键词推送", "websocket发送(已关闭)", e)

Although it works, I don’t recommend you use it like me.

@misss85246 that sort of looks like in premise what is happening here: Sanic based websocket pubsub feed · GitHub. The idea behind this example is to create a registry of connected clients similar to what you are doing. I think it is a valid pattern as long as you keep in mind how it can scale horizontally (if needed).

Are you Chinese? Chinese appears in the code

@wangjianweiwei yeah, I’m Chinese :cowboy_hat_face:

原来是中国老乡啊,你这个头像我都没敢认 :joy:

@misss85246 has been a part of translating our documentation. It will be out Sunday :muscle:

I’m Chinese too! Welcome!