Search code examples
pythonasynchronoustornadopython-asyncio

Combine Asyncio Tcp Server and Tornado Web Socket


I have a asycnio tcp server which is connected to 5 tcp client.

import asyncio

class EchoServerClientProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        peername = transport.get_extra_info('peername')
        print('Connection from {}'.format(peername))
        self.transport = transport

    def data_received(self, data):
        message = data.decode()
        print('Data received: {!r}'.format(message))

        print('Send: {!r}'.format(message))
        self.transport.write(data)

        print('Close the client socket')
        self.transport.close()

loop = asyncio.get_event_loop()
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)

# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()

I also have a tornado websocket which is connected to 3 webbrowser.

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

class WSHandler(tornado.websocket.WebSocketHandler):

    clients = []

    def check_origin(self, origin):
        return True

    def open(self):
        print('New Connection Established')
        self.write_message("Connected Server...")
        WSHandler.clients.append(self)

    def on_message(self, message):
        print('Message Received: {}'.format(message))

    def on_close(self):
        print('Connection Closed...')
        WSHandler.clients.remove(self)
        del self

    @classmethod
    def write_to_clients(cls, message):
        print("Writing to Clients")
        for client in cls.clients:
            client.write_message(message)

    @classmethod
    def write_to_other_clients(cls, self_client, message):
        print("Writing to Other clients")
        for client in cls.clients:
            if  self_client != client:
                client.write_message(message)


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



http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Now I want to combine them.Here's what I want to do: My server will always listen to my clients. When data arrives from any of them, Program will be posted with Websocket to webbrowser asynchronously. I did a lot of research, But couldnt reach the success...


Solution

  • You can use the bridge between asyncio and tornado provided by tornado:

    # Imports
    import asyncio
    import tornado.platform
    # [...]
    
    # Define Tornado application
    class WSHandler(tornado.websocket.WebSocketHandler):
        clients = []
        # [...]
    application = tornado.web.Application([(r'/', WSHandler)])
    
    # Define asyncio server protocol
    class EchoServerClientProtocol(asyncio.Protocol):
        def data_received(self, data):
            WSHandler.write_to_clients(data.decode())
        # [...]
    
    # Install asyncio event loop
    tornado.platform.asyncio.AsyncIOMainLoop().install()
    loop = asyncio.get_event_loop()
    
    # Create tornado HTTP server   
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    
    # Start asyncio TCP server
    coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8889)
    server = loop.run_until_complete(coro)
    
    # Run forever
    loop.run_forever()
    # [...]