Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I send a websocket message in Tornado at will?

I'm very new to Tornado and wanted to know if it is possible to send a message (write_message) at will from within my Python program to all clients? For example, say my program is monitoring a directory to see if a file appears/exists. When it appears, I want to send a web socket message to a browser client that the file exists. I can't seem to understand how to invoke the "write_message" method without first receiving a websocket message (on_message handler.)

Even if I use the "PeriodicCallback" method, I'm still unclear as to how I actually call the "write_message" method. Are there any examples out there of how to invoke "write_message" without doing it within the "on_message" handler?

like image 945
GregH Avatar asked Sep 07 '15 20:09

GregH


People also ask

How do you use a tornado WebSocket?

If you map the handler above to /websocket in your application, you can invoke it in JavaScript with: var ws = new WebSocket("ws://localhost:8888/websocket"); ws. onopen = function() { ws. send("Hello, world"); }; ws.

Can a WebSocket server send message to client?

Messages sent by the server to the client can include plain text messages, binary data, or images. Whenever data is sent, the onmessage function is fired. This event acts as a client's ear to the server. Whenever the server sends data, the onmessage event gets fired.

Are WebSocket messages delivered in order?

All messages are delivered to subscribers in order until the WebSockets connection drops.


1 Answers

You need to keep a collection of openned websockets and iterate through that collection at will to send messages.

As an example, I'll send a message anytime a client connect to your.domain.example/test/ but the idea is the same whenever you want to send something:

import os.path
import logging

from tornado import ioloop, web, websocket


SERVER_FOLDER = os.path.abspath(os.path.dirname(__file__))
LOGGER = logging.getLogger('tornado.application')


class TestHandler(web.RequestHandler):
    def get(self):
        server = ioloop.IOLoop.current()
        data = "whatever"
        server.add_callback(DefaultWebSocket.send_message, data)
        self.set_status(200)
        self.finish()


class DefaultWebSocket(websocket.WebSocketHandler):
    live_web_sockets = set()

    def open(self):
        LOGGER.debug("WebSocket opened")
        self.set_nodelay(True)
        self.live_web_sockets.add(self)
        self.write_message("you've been connected. Congratz.")

    def on_message(self, message):
        LOGGER.debug('Message incomming: %s', message)

    def on_close(self):
        LOGGER.debug("WebSocket closed")

    @classmethod
    def send_message(cls, message):
        removable = set()
        for ws in cls.live_web_sockets:
            if not ws.ws_connection or not ws.ws_connection.stream.socket:
                removable.add(ws)
            else:
                ws.write_message(message)
        for ws in removable:
            cls.live_web_sockets.remove(ws)


def serve_forever(port=80, address=''):
    application = web.Application([
            (r"/test/", TestHandler),
            (r"/websocket/", DefaultWebSocket),
            ...
        ],
        static_path=os.path.join(SERVER_FOLDER, ...),
        debug=True,
    )
    application.listen(port, address)
    LOGGER.debug(
            'Server listening at http://%s:%d/',
            address or 'localhost', port)
    ioloop.IOLoop.current().start()


if __name__ == "__main__":
    serve_forever()

You'll obviously need to create a websocket in the browser using the following JavaScript:

socket = new WebSocket('ws://your.domain.example:80/websocket/');

And manage it accordingly.

like image 184
301_Moved_Permanently Avatar answered Oct 07 '22 15:10

301_Moved_Permanently