flask-uwsgi-websocket icon indicating copy to clipboard operation
flask-uwsgi-websocket copied to clipboard

How can I send something to a specific websocket in my application from another function?

Open svenstaro opened this issue 9 years ago • 7 comments

Imagine this:

@websocket.route("/websocket")
def my_websocket(ws):
    ws.send("foo")

@app.route("/somewhere")
def somewhere():
    #ws.send("bar")

Is this way of working at all possible with websockets? Do I instead need some kind of internal message spooler in my_websocket that I send stuff to which in return sends it to the client? Or can I somehow do this more conveniently as I outlined in my example?

svenstaro avatar May 11 '15 01:05 svenstaro

If it's a single instance, you can just keep a set of connections. Otherwise you should use redis or something like that for pub/sub.

zeekay avatar May 11 '15 01:05 zeekay

Can you post up a snippet? I tried keeping connections in a global list but that didn't work out so well.

svenstaro avatar May 11 '15 10:05 svenstaro

I would personally use redis pub/sub for this. Subscribe to an event (for example "send-to-ws") when the websocket connects. Then publish that event in the "somewhere" route. This way you don't need global lists and it scales great.

You can use Flask-Redis and redis-py for this: https://github.com/underyx/flask-redis https://github.com/andymccurdy/redis-py

Use the redis.pubsub() method.

I wouldn't try to keep connection instances in a global, because those won't be shared across instances, if you for example run uWSGI with several workers.

jaapz avatar May 15 '15 08:05 jaapz

When I try to do something like:

@app.route("/ready")
def ready():
    ws.send("message")
    return ""

It seems to send nothing through the websocket and instead sends "message" to the browser that requested /ready.

Necavi avatar May 26 '15 23:05 Necavi

If you are running one instance of the app (not multiple, like when you use uWSGI), something like this should work:

app = Flask()
websocket = WebSocket(app)
clients = {}

@websocket.route('/connect')
def connect(ws):
    clients[ws.id] = ws

    # keep listening for messages until the connection is closed
    while ws.connected is True:
        message = client.receive()

    # the client is removed when the websocket connection is closed
    del clients[ws.id]

@app.route('/send_all_websockets')
def send_all_websockets():
    """ send a message to all connected websockets """
    for id, client in clients.iteritems()
        client.send('hello')

But again, this will only work when running as one instance. This is also basically what happens in the chat example: https://github.com/zeekay/flask-uwsgi-websocket/blob/master/examples/chat-gevent/chat.py

jaapz avatar May 27 '15 11:05 jaapz

Ahh, that's pretty much exactly what I did, I had figured it was a problem with doing it through uWSGI, that at least gives me somewhere to look, thanks!

Necavi avatar May 27 '15 18:05 Necavi

...really should add another example to cover this (extremely common) use case.

zeekay avatar Jun 12 '15 08:06 zeekay