Weekend Project (Part 2): Turning Flask into a real-time websocket server using Flask-SocketIO

Josh Porter
Hacker Valley Studio
9 min readDec 4, 2017

If you haven’t already, you can read Part 1 of this series here.

Agent and Spymaster views on mobile

Since you’re reading this blog post, I’ll assume you’re no stranger to Python and modern web technologies. In the Python community, there’s a plethora of frameworks for building web applications, APIs, and websites. To name a few… Flask, Django, Pyramid, Bottle — these are all very popular for building applications of any scale and any variety. Personally, I only have experience with Flask, and limited experience with Django. Flask is a very versatile and lightweight framework that can be molded and twisted to fit many use cases. This includes building basic websites, web APIs, CRUD applications, or in the case of this blog post, a websocket server.

In my experience, Flask makes a great server for rendering JSON responses and templated HTML using Jinja2. It’s a very lightweight and manageable framework for these use cases, making it very popular in the Python community. However, when it comes to event-based websocket servers, the community tends to lean more on Node.js. Node’s major draw is the event loop — a continuously running loop that executes “events” or actions as they’re queued. This allows for a very flexible and powerful concurrency model when compared to Python’s typical multi-threading. This “event loop” is something that is required to convert Flask into an “evented” websocket server.

The fundamentals behind this requirement are due to the Python interpreter’s “blocking” nature. Typically, when Flask receives an HTTP request, it executes a function, and the server has to wait for this function to finish before accepting another request. Transforming Flask into an asynchronous evented beast allows it to process requests faster, independent of one another. An evented Flask server can accept a request and process it while accepting another request asynchronously. This is a behavior we’ll need for websockets, since there will be the possibility of Flask being hammered with websocket messages.

Getting Started

If we look back at the diagram from Part 1, take note of the few things Flask is responsible for. Flask’s primary purpose is to manage our game lobbies and to send/receive data via websockets. In addition to acting as our “API,” we’ll also be using Flask to serve out our compiled front-end code. This is a bit of a hack, but I chose to do it this way so the app can be run using only Flask. The alternative is to leave the compiled front-end code in a static directory and serve it using a standard web server such as Apache or nginx, but I wanted to leave that as an optional requirement.

Flask Refresher

If you’re new to Flask or haven’t used it in a while, here’s a quick run-down of how it works.

Before jumping into anything, we have to install our dependencies. Let’s go ahead and install all of the dependencies for a websocket Flask server.

pip install flask flask-socketio eventlet

Now, let’s dive into creating our Flask app. Flask requires a few things — the main app script — I’m calling this one flask-simple.py, and a templates folder for our HTML Jinja templates. Optionally, you can add a static directory to store your Javascript, CSS, and other static files loaded by the application. For the sake of simplicity, we’ll omit that folder for this project.

Example Flask folder structure

Now that our application structure has been established, we need a script to run it. Let’s edit our flask-simple.py file.

As you can see, there’s really not a lot of work to do to get Flask up and running! After importing the required modules, there are three main things that we do. First, we initialize a Flask object. Then, we set up our index route, which will route us to localhost:5000/ in the browser. The decorator @app.route() wraps our index function with some Flask code that essentially tells it to use the output of this function as the response. In this case, we’re rendering a template called index.html. The final piece of this code is our main function, which runs the Flask server in debug mode, allowing us to receive more verbose console output and enables hot-reloading.

Our index.html file needs a bit of work so we can show something in the browser to prove that Flask is doing its job. Let’s just edit it and enter some basic Hello World HTML.

Now we’re ready to run our app! Type python flask-simple.py and load up http://localhost:5000 in a browser window. You should see “Hello World!” in large bold letters. If your app renders a blank page or returns an HTTP error, check the terminal for any error output. Make sure all files are named properly and free of syntax errors, and make sure you installed the dependencies!

Introducing Websockets

If you’re a stranger to websockets, it can be a bit overwhelming to understand at first. Websockets work similarly to polling using AJAX. Think of the notification icons on many social media websites — as you’re sitting on the page and a new notification rolls in, the page instantly knows about it and displays an icon notifying you. The page has to get this info from somewhere without you refreshing, right? One way to do it is to use standard AJAX requests and ping the server every 15 seconds for new notifications. This can be a bit clunky… there must be a better way to do it, right?

This is where websockets can come in handy. A websocket is a persistent connection between your browser and the server that messages can be sent across. Establishing this connection requires a simple handshake between the server and the client, after that the server can essentially push messages to the client. Using a websocket connection will allow our server to “push” notifications to our client, rather than our client polling for updates. This results in a more seamless system for receiving new data from the server.

Diagram of polling AJAX vs Websockets

Similar to click handlers, we have callback functions that run when a new message comes in that we care about. These handlers will process messages on a specific channel that are delivered via the websocket.

socket.on('notification', function(msg) {
console.log('New notification! ', msg);
});

As messages come across the websocket on the connect channel, our callback will be called and will have access to the full JSON message.

Namespaces, the typical way of setting up a channel using websockets, have no way of separating users. A message from the server on a namespace will send to all connected clients with listeners for that namespace. To separate our users, we will use “rooms.” Rooms in Socket.IO are just like chat rooms — users can join/leave and send messages. Rooms have a unique identifier, so users can join a unique room and receive messages intended only for users connected to that room.

Socket.IO Namespaces vs. Rooms

In the context of our Codenames game, users will join unique rooms to receive game updates. Using a namespace doesn’t fit well here, because our game updates will be sent to all connected clients listening on that namespace, which would cause their games to become corrupted and display the wrong set of words!

Getting Flask working with Websockets

To convert Flask to use websockets, we first need to import a few things from Flask-SocketIO. We’re going to leave our /index as a standard Flask route since we’ll be using it to actually render the index template. In our app, there are a few actions we’ll want to do using websockets — create a room, join a room, and take a turn. Instead of creating standard Flask routes, we’ll need to create some websocket event handlers. Before doing so, our Flask app needs to be wrapped by Socket.IO. Instead of starting the app with app.run(), we’ll instead use our Socket.IO instance and call socketio.run().

EDIT: This example uses pieces from my codenames repo. To replicate this example for your own project, remove the import and any references to game and replace with your own data structures (dictionary, list, string, etc).

Notice how our decorator for on_create() differs from the decorator for index(). This is because we’re leveraging the Socket.IO decorator to create a websocket event handler instead of a standard Flask route. This works in a similar fashion to the standard Flask routes, except instead of speaking with GETs and POSTs, we’re speaking using rooms and channels over a websocket connection. When the client sends a message on the create channel, our on_create handler will process it.

Our handler does a couple of things — it initializes a game object, joins a “room”, and emits a message on the join_room channel with the room identifier. This allows the client to subsequently send a join message with that ID to join the room. Utilizing unique rooms, we will be able to keep our Codenames clients all up to date with a single send call.

The final step is on line 28, where we call socketio.run() instead of app.run(). Again, this is due to us wrapping our app with the SocketIO object.

Making sure everything is working…

To demonstrate our working websocket server, in our index.html we’ll write a little bit of vanilla Javascript to send a websocket message to our Flask server to solicit a response. We’ll create a button with a simple onclick action that simply emits a message on the create channel telling the server to create a game using some default parameters. In addition to that button, we’ll want to verify our communication channel by subscribing to the join_room channel and logging the output. This will confirm that our client and server are communicating properly.

Now, as with our basic Flask example, start the app using python flask-socketio.py and open up http://localhost:5000 in a browser window. We should see a plain webpage with a “Create Game” button, with the words “Websocket connected!” in the Javascript console window.

And voila! Our Flask server and client are now speaking in a new language! Well, the same old language, just over a new medium… websockets! As you can see in the image below, our logged output shows our expected outcome — establish the connection, click the button, send the create message, and receive the join_room response.

Test index.html using Socket.IO

Now that we’ve got our Codenames game lobbies created, there’s only two more actions to add to our server — join_room and flip_card. It’s as simple as it was to write our create endpoint — define the function, add the decorator, and send our message.

The lines to take note of here are lines 10 and 20. In our on_create handler, we used the emit function to send a message back to the client on the join_room channel. This was because we hadn’t joined a room just yet. In this example, we use the send function with the room parameter. Instead of sending a message on a specific channel, this will send the message to all connected members of our room. In line 9, the server adds us to the designated room — we will now receive all broadcasts using send for the room with the ID we sent to the server.

Thanks for reading along and sticking with me through this journey! We’re about halfway to a fully functioning Codenames game — in the next installment, we’ll ditch that vanilla Javascript code and work towards integrating websockets into a larger-scale Vue.js app using Vuex.

If you have any questions or just want to chat, join our Slack channel!

Update 2018/01/23 — Read about scaffolding Vue with vue-cli and integrating Vuex in Part 3 of this series!

--

--

Josh Porter
Hacker Valley Studio

Baltimore-based software developer with a passion for clean code, craft beer, and video games. http://codecaffeinated.com