Creating Websockets Chat with Python
In this post I’m going to write simple chat roulette application using websockets. App will consist of very basic user interface with some HTML + JavaScript. When I say “basic” I really mean it, it’s going to be just input box and vanilla JS creating websocket connection. On the backend side app will have websocket server managing realtime communication between clients.
Websockets are one of the coolest technologies in recent years. They are getting popular mostly because they allow two-way communication between server and browser. In traditional HTTP application client sends requests and server issues response after which their exchange is terminated. This model is totally okay for most web apps, but it is inefficient for applications that require realtime communication. RFC 6455 is probably most detailed introduction to websockets specs.
If you’d like to write websocket applications in Python there are couple of choices. If you’re Django user there are Channels, for Flask there is flask-SocketIO. Both solutions are trying to extend existing web frameworks to allow for usage of websockets. Python Tornado on the other hand is a whole web framework built for realtime asynchronous applications using websockets.
One of the most mature implementations of websockets is Autobahn-Python. Autobahn websockets implementation supports both Twisted and Asyncio. I’m going to use Twisted implementation. Why do I think Autobahn + Twisted is worth writing about?
- Twisted is oldest and most stable asynchronous solution for Python, it is still actively developed (e.g. just recently most components finally gained Python 3 support) and still grows quite quickly (e.g. there is work on adding HTTP2 support to Twisted)
- Twisted is built with asynchronous model at the core, this is absolutely crucial for websocket applications that need to deal with long-living persistent connection from client
Hello websocket
Before we actually start with development of server side websockets we’ll need to set up something that is going to serve index.html file with client side JavaScript + HTML handling user interaction with your websocket server.
Serving static file with Twisted is trivial and looks like this.
Save this as server.py and create index.html file in same directory. Index.html can be blank for now, we will write HTML in a moment.
Now let’s actually add some websockets to the mix.
Above code adds simple websockets protocol that is just responding to every message with pretty stupid message: “message received”. It’s no big deal, but it’s pretty nice because at this point you actually have working websockets server. There is no client side websockets code yet, but you can test your server with some command line websockets clients or browser extension, e.g. with “Simple WebSocket Client” Chrome extension. Just run your server.py and ping ws://localhost:8080/ws from Chrome extension.
Add client side JavaScript
Now that we have working websockets server we can create our client. We need two things: input box where user can write some strings that are going to be transmitted to server; and JavaScript code creating websockets connection and sending data to our websockets server after some UI event occurs.
Mozilla Developer Network has some good docs about this topic, I’m going to use vanilla JS, but you can just as well use jQuery or even some specialized library for websockets (e.g Socket-IO).
Below is our index html. Our JS code does following things. First it creates websocket instance and defines some event listener that will tell browser what to do when websocket message is received. When websocket message is received browser should simply update “output” node with text content of message. We then fetch input box, add event listener to “submit” event. When “submit” event happens browser should use our websocket and send message via this socket. Sending data is just a matter of making mySocket.send call on WebSocket object.
At this point we have simple websockets server and client that talk to each other. Their communication is not very complex. Server just echoes back message from client. At this point we can start adding some cool features.
Register and unregister clients
Now that we have basic skeleton of websockets project we can start adding some real functionality. First thing we need to do is register and unregister clients starting conversations with our server. To accomplish this we will need to add some factory to our protocol. In Twisted protocols are created per connection, and they allow you to define event listeners for your application. In case of websockets this means that your protocol can define event handlers for common scenarios: message being sent, connection being made, connection lost etc. Factories on the other hand manufacture protocols. They are common to multiple protocols, they define how protocols should interact with each other.
In case of our chat roullette all this means that aside from writing protocol we just need to write factory that will define how websocket clients will interact with each other. Of course we also need to define protocols to specify how are we going to handle typical websockets events.
Let’s start with protocol. Our base class will look like this, no real code for now just docstring and basic structure of our object.
Implementation of our protocol would look like this:
Now that we have our protocol we need to define common functionalities per protocol and add a way to manage interactions between protocols. Our base protocol factory could look like this.
and implementation of this could look like this:
Now that we have everything defined you only need to tie it together, create instances of objects and start your program:
You can find full Python source code here, HTML with JS is here.
With the above code you should be able to talk to yourself via your Chat server. Just open couple of browser tabs and start writing in each input box. There is probably lots of things that could be improved, but I just wanted to create very basic demo that could get people started. If you do find some bugs or mistakes feel free to ping me.