Search code examples
pythonloopswebsockettornado

python - handle tornado connections into while loop


I have a server running a loop that reads data from a device and I want to send them to all clients who connect on a websocket on tornado. I tried putting the loop inside the open function but then it can't handle on_close function or new connections.

What is best practice to do that?

#!/usr/bin/env python

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket

class MyWebSocketServer(tornado.websocket.WebSocketHandler):
    def open(self):
        print('new connection'+self.request.remote_ip)
        try:
            while True:
                '''
                read and send data
                '''
        except Exception,error:
            print "Error on Main: "+str(error)

    def on_close(self):
        print('connection closed'+self.request.remote_ip)

application=tornado.web.Application([(r'/ws',MyWebSocketServer),])

if __name__=="__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8000)
    print('start')
    tornado.ioloop.IOLoop.instance().start()

Thanks


Solution

  • Here's a full example about running your blocking code in a separate thread and broadcasting messages to all connected clients.

    ...
    
    from concurrent.futures import ThreadPoolExecutor
    
    executor = ThreadPoolExecutor(max_workers=1) # spawn only 1 thread
    
    
    class MyWebSocketServer(tornado.websocket.WebSocketHandler):
        connections = set() # create a set to hold connections
    
        def open(self):
            # put the new connection in connections set
            self.connections.add(self)
    
        def on_close(self):
            print('connection closed'+self.request.remote_ip)
            print('new connection'+self.request.remote_ip)
            # remove client from connections
            self.connections.remove(self)
    
        @classmethod
        def send_message(cls, msg):
            for client in cls.connections:
                client.write_message(msg)
    
    
    def read_from_serial(loop, msg_callback):
        """This function will read from serial 
        and will run in aseparate thread
    
        `loop` is the IOLoop instance
        `msg_allback` is the function that will be 
        called when new data is available from usb
        """
        while True:
            # your code ...
            # ...
            # when you get new data
            # tell the IOLoop to schedule `msg_callback`
            # to send the data to all clients
    
            data = "new data"
            loop.add_callback(msg_callback, data)
    
    ...
    
    if __name__ == '__main__':
        loop = tornado.ioloop.IOLoop.current()
    
        msg_callback = MyWebSocketServer.send_message
    
        # run `read_from_serial` in another thread
        executor.submit(read_from_serial, loop, msg_callback)
    
        ...
    
        loop.start()