user2227379
user2227379

Reputation: 3

How to broadcast a message to all geventwebsocket clients

I'm setting up a geventwebscoket app in python using gevent-websocket.

In on of the examples (chat-app) that is pretty much the same as my application, I define an app that handles websocket connections and messages like this:


import json

from gevent import monkey
monkey.patch_all()

from flask import Flask, render_template
from werkzeug.debug import DebuggedApplication

from geventwebsocket import WebSocketServer, WebSocketApplication, Resource

flask_app = Flask(__name__)
flask_app.debug = True


class ChatApplication(WebSocketApplication):
    def on_open(self):
        print("Some client connected!")

    def on_message(self, message):
        if message is None:
            return

        message = json.loads(message)

        if message['msg_type'] == 'message':
            self.broadcast(message)
        elif message['msg_type'] == 'update_clients':
            self.send_client_list(message)

    def send_client_list(self, message):
        current_client = self.ws.handler.active_client
        current_client.nickname = message['nickname']

        self.ws.send(json.dumps({
            'msg_type': 'update_clients',
            'clients': [
                getattr(client, 'nickname', 'anonymous')
                for client in self.ws.handler.server.clients.values()
            ]
        }))

    def broadcast(self, message):
        for client in self.ws.handler.server.clients.values():
            client.ws.send(json.dumps({
                'msg_type': 'message',
                'nickname': message['nickname'],
                'message': message['message']
            }))

    def on_close(self, reason):
        print("Connection closed!")


@flask_app.route('/')
def index():
    return render_template('index.html')

WebSocketServer(
    ('0.0.0.0', 8000),

    Resource([
        ('^/chat', ChatApplication),
        ('^/.*', DebuggedApplication(flask_app))
    ]),

    debug=False
).serve_forever()

I want to have some scheduled processes in my code that send a message to every client connected to the websocket. In the examples and the limited documentation I find no way of calling the broadcast method from somewhere else in the project. Every message/broadcast has to be sent as a reaction to a received message (as I understand it).

I tried to figure it so I tried broadcasting a message every time someone visits the index page:

@flask_app.route('/')
def index():
    chat_application = ChatApplication()
    chat_application.broadcast("A new user on the page!")
    return render_template('index.html')

This throws an error:

chat_application = ChatApplication()
TypeError: __init__() missing 1 required positional argument: 'ws'

Long story short: I do not know how to send a message to every client on the websocket since I need the ChatApplication instance to access the broadcast function and I can't seem to figure out how to create a ChatApplication object to let me call that function.

Upvotes: 0

Views: 686

Answers (2)

srepmub
srepmub

Reputation: 104

using the above, I ran into the following:

for client in server.clients.values(): RuntimeError: dictionary changed size during iteration

this seems a real problem, as some client may be connecting/disconnecting during this for loop. I think adding a list() around server.clients.values() should avoid this, but I haven't tested it thoroughly

Upvotes: 0

user2227379
user2227379

Reputation: 3

I figured it out.

By starting a server like this

server = WebSocketServer(
    ('0.0.0.0', 8000),

    Resource([
        ('^/chat', ChatApplication),
        ('^/.*', DebuggedApplication(flask_app))
    ]),

    debug=False
)

server.serve_forever()

you can access all the clients and send them a message like this

for client in server.clients.values():
    client.ws.send("whatever you want to send") 

Upvotes: 0

Related Questions