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.