Getting started with WebSockets in Tornado

-

In this article, we’re going to build a simple chat application with WebSockets, using the Tornado framework in Python. You can clone the GitHub repository and try it out.

1. Getting started

First, follow the “Installation” steps on Tornado’s website.

Specifically, run pip install tornado.

Next, go ahead and create a file named chat.py with this starter code:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

You can run this server with python3 chat.py. If you navigate to http://localhost:8888, it should give an internal server error, since we haven’t written the index.html page yet.

2. Writing the index.html page

Let’s define a WebSocket with the following specification. It will be located at the route /websocket (that is, http://localhost:8888/websocket), and it takes a JSON dictionary with two key-value pairs: one key will be message, and the other key will be user. It sends a JSON dictionary to clients with the same format.

For example, we might make a request by sending the following dictionary to /websocket:

{
    “message”: “Hello world!”,
    “user”: “Neel”
}

(Obviously, in practice, we would require some sort of authentication.)

Then we’ll make the page prompt the user to give a username on load. When a user sends a message, we’ll send the name that they enter as the value for “user”, and their message as the value for “message.” When the user receives a message, we’ll append it to a div on the page. Here’s some quick-and-dirty sample HTML (which we’ll save as index.html) that gets the job done:

<!DOCTYPE html>
<html>
<head>
    <title>Chat application</title>
</head>
<body>
    <div style="width:100%; padding: 20px; overflow-y: scroll;">
    <div id="messages"></div>
    <div style="padding-top: 20px;">
        <form onsubmit="return sendMessage()">
            <input id="message" type="text" style="width: 70%;"><button style="width: 25%">Send</button>
        </form>
    </div>
    <script>
    var ws = new WebSocket("ws://localhost:8888/websocket");
    var username = prompt("What's your name?");

    function sendMessage() {
        var messageInput = document.getElementById("message");
        var message = messageInput.value;
        var payload = {
            "message": message,
            "user": username
        }
        // Make the request to the WebSocket.
        ws.send(JSON.stringify(payload));
        // Clear the message from the input.
        messageInput.value = "";
        return false;
    }

    ws.onmessage = function(evt) {
        var messageDict = JSON.parse(evt.data);
        // Create a div with the format `user: message`.
        var messageBox = document.createElement("div");
        messageBox.innerHTML = messageDict.user + ": " + messageDict.message;
        document.getElementById("messages").appendChild(messageBox);
    };
    </script>
</body>
</html>

3. Writing the WebSocket

Now, we’ll implement the /websocket route, by modifying the chat.py file accordingly:

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

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

class SimpleWebSocket(tornado.websocket.WebSocketHandler):
    connections = set()

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

    def on_message(self, message):
        [client.write_message(message) for client in self.connections]

    def on_close(self):
        self.connections.remove(self)

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/websocket", SimpleWebSocket)
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

And we’re done! You can run the server with python3 chat.py, and try out the chat box by opening http://localhost:8888 in multiple tabs. Let me know if you have any questions or other ideas in the comments.

Tags: tornado python websockets simple

See also: How to SSH tunnel to a Docker container on a remote server

Back to all posts

Neel Somani

About the Author

I'm the founder of Eclipse. You can follow me on Twitter.