jgv115
jgv115

Reputation: 509

Tornado websocket messages aren't receiving

I have a very simple setup inspired by this question: Tornado - Listen to multiple clients simultaneously over websockets

Essentially, I have one Websocket Handler that may connect to many websocket clients. Then I have another websocket handler 'DataHandler' that will broadcast a message everytime it receives a message.

So I made a global list of TestHandler instances and use it to broadcast messages to all the instances

ws_clients = []

class TestHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print('open test!')
        ws_clients.append(self)
        self.random_number = random.randint(0, 101)

    def on_message(self, message):
        print(message)

        print('received', message, self, self.random_number)
        self.write_message('Message received')

    def on_close(self):
        print('closed')


class DataHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print('data open!')

    def on_message(self, message):
        for c in ws_clients:
            c.write_message('hello!')


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/test_service/", TestHandler),
            (r"/data/", DataHandler),
            (r"/", httpHandler)
        ]

        tornado.web.Application.__init__(self, handlers)


ws_app = Application()
ws_app.listen(8000)
tornado.ioloop.IOLoop.instance().start()

TestHandler can receive messages fine through the address ws://127.0.0.1/test_service/ and DataHandler can receive messages fine through the address ws://127.0.0.1/data/ but whenever I loop through ws_clients, I never receive any messages on TestHandler.

Am I doing something wrong?

Upvotes: 1

Views: 623

Answers (1)

xyres
xyres

Reputation: 21744

Here's what I'd do - I'd create a new method on TestHandler which will serve one single purpose - take a message and send it to all the connected clients.

Before going into the code, I'd like to point out that it seems (conventionally) better to keep ws_clients inside the class instead of a global object. And use a set instead of a list.

class TestHandler(...):
    ws_clients = set() # use set instead of list to avoid duplicate connections

    def open(self):
        self.ws_clients.add(self)

    @classmethod
    def broadcast(cls, message):
        """Takes a message and sends to all connected clients"""
        for client in cls.ws_clients:
            # here you can calculate `var` depending on each client
            client.write_message(message)

    def on_close(self):
        # remove the client from `ws_clients`
        self.ws_client.remove(self)


# then you can call TestHandler.broadcast
# from anywhere in your code
# example:

class DataHandler(...):
    ...

    def on_message(self, message):
        # pass the message to TestHandler
        # to send out to connected clients
        TestHandler.broadcast(message)

Upvotes: 3

Related Questions