Imagine building a Web Application that requires real-time interaction with the server. Is it even possible? Do you know that building a web application is not only about HTTP Requests and HTTP Responses. We might think that HTTP Requests and Responses aren’t real-time interaction with the server. The server only waits for any requests from the client, processes them, return responses, then done. WebSocket can do more than this, even the server can send data without the client requesting it first, cool right?
What’s Wrong with HTTP?
HTTP (HyperText Transfer Protocol) is a protocol which allows the fetching of resources. We might think that HTTP can do most of the jobs in Web Applications. But what’s wrong here?
Don’t worry about that, HTTP is fine by itself. The only thing you need to know is that there is one thing that HTTP can’t do by itself, Real-Time Interactions. HTTP is a strictly unidirectional protocol. Which means, we are pulling data from server, or requests are initiated by the recipient, usually the Web browser (the server is passive).
Before WebSocket, real-time applications can be built with HTTP by doing long polling.
I’ll give an example to explain what Long Polling is. Imagine there are one client and one server. The process is same as the normal HTTP request. The client requests to the server. The difference is that the server doesn’t immediately reply to the client. It holds the requests until certain time or until the server gets what’s the client needs.
From this scenario, I think you’ve already know that the requests are being held by server. The server elects to hold a client’s connection open for as long as possible, responses are given only after data becomes available or a timeout threshold is reached. The drawback is that the resources are tied-up by certain clients.
WebSocket is invented to solve previous problems. It lays on top of a device’s TCP/IP stack. Unlike HTTP, WebSocket provides bidirectional and full-duplex communication between Client and Server, which operates over HTTP through a single TCP/IP socket connection.
To interact with the server in real-time, the client requests HTTP upgrade connection to open the WebSocket connection with the Server. We can see that WebSocket connection is initially made from HTTP requests that upgrades the current connection.
Then, the server will accepts and upgrade the connection. Thus, handshake is made and then the two can communicate each other via WebSocket.
The server in WebSocket connection won’t only listen and gives responses to the client, but it can also send message to the client independently without any trigger from the client.
Channels is a library that can be integrated and upgrade our Django project so it can handle WebSocket connections or both HTTP and WebSocket connections.
In order to upgrade our Django project with Channels, we need to install channels dependency first.
pip install channels
In this example, we’ll see simple chat application with Django Channels. I’ll walk the general idea to integrate our Django project with Channels, so you can know the big ideas and where to continue after reading this article.
First, we need to specify channels in our root of INSTALLED_APPS (so it can take control of runserver command).
Then, we need to modify our asgi.py to serve WebSocket connections.
As you see, WebSocket has its own routing pattern, that starts with ws:// or wss://. Now, let’s define our WebSocket endpoint patterns in our app:
With this pattern, our client can connect to our server through ws(s)://<host>/ws/chat/<room_name>/.
Now we’re done with the general configurations. Because we’re building a chat application, and don’t want to lose the chat history, we need to store it somewhere, the most used approach is to integrate Redis into our Channels. It is in the separate package, so we need to install it first.
pip install channel_redis
You may be wondering, what are Channel Layers? Channel Layers allow you to talk between different instances of an application. To put it simply, we can think of them as group rooms that we’ve subscribed to. Imagine you’re sending messages to your friend in a Group Chat A. It won’t be delivered to Group Chat B, right? Only participants in Group Chat A can read the message. The same goes for Channel Layers, we can have many rooms to subscribe to.
Now that we have our Channel Layers ready, we can setup our Django’s WebSocket Consumer to connect with clients.
Because WebSocket is a full-duplex communication, there are handshakes on open and on close connection with the client. Because we’re building a chat application, on open connection, we will create a group chat with room_name given through URL, and delete it on disconnect.
What happens when one client send chat to other clients through WebSocket (implemented in chat_message function)? Our WebSocket consumer will listen to it and broadcast the message to other connected clients in the room (implemented in receive function).
Now, let’s implement the frontend’s side.
Here we take the room name passed by user, then open WebSocket connection to our Django Channels. Our client-side will listen to messages that are sent by our Django through onmessage event.
Let’s take a look at the result. First, create a room.
We can see that messages from one client broadcasted by WebSocket to another.
ASGI over WSGI
If you’re Django’s lovers, then you might have noticed that we used ASGI instead of WSGI to serve both HTTP and WebSocket requests. So what are they?
WSGI (Web Server Gateway Interface) applications take a single request and return response at a time. This single and synchronous callable limits WSGI for long lived connections like WebSocket connections.
On the other side, ASGI (Asynchronous Server Gateway Interface) is a successor of the successful WSGI. ASGI’s goal is to continue become the standard compatibility between web servers, frameworks and applications like WSGI in asynchronous python. With ASGI, you can do most of the asynchronous jobs with Django (generally Python-based Web Server), since WebSockets are also performed asynchronously.
Implementation in Software Engineering Project Course 2021
Our Team — Magic People — uses Django Channels to handle both HTTP and WebSocket connections. Before the fifth sprint, our backend project only handles HTTP requests. There is a requirement to update statistics on Admin’s Home Page in real-time.
With the same configurations as before, our team can upgrade the Django project with Channels. I’ll show you one of the example in our team’s Backend Project. It is to update our weekly income’s Chart when there are any new transactions approved by admin. The difference from previous configurations is that there are extra code to send this information when there are any new transactions approved.
Here, we get the channel_layer object then send through a group room named admin_dashboard, and send the message as JSON String.
Let’s see on the go!
I think WebSocket is a good solution instead of HTTP’s Long Polling if you want to build real-time Web Applications. Long polling is much more resource intensive on servers whereas WebSockets have an extremely lightweight footprint on servers. Long polling also holds up connection and lock resources. WebSocket is a full-duplex asynchronous messaging. In other words, both the client and the server can stream messages to each other independently.
I prefer to use WebSocket and have implemented it in our Project. WebSocket is very interesting because it can bring full-duplex messaging into Web Application Programming. You have to try it!
Thank you for reading, Happy Coding!