Reputation: 3
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
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
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