WebSockets and Real-Time Applications

Emily Lim
10 min readMar 7, 2021

--

An intro guide to WebSockets

Preface

As a developer, (especially a new one like me) it can be intimidating opening yourself up to learning about the world of technology. Every day I often find myself saying “What the heck is that?!”. And every day, after doing a little research on my previously stated conundrum, I also find myself saying “Wow, that is so neat!”. (I’m serious I do say that…) However, that is what I love most about becoming a software developer, the continuous learning that it provides me with. Speaking of continuous learning… one of those topics that gave me a big question mark over my head was WebSockets. So, I thought I would write an intro guide on what I learned and how they are used in real-time applications. Cue the lights, the music, …. let’s get into WebSockets.

So what exactly is a WebSocket?

Well person of the internet, I am glad you asked. According to the MDN Web Docs the WebSocket API is …

… an advanced technology that makes it possible to open a two-way interactive communication session between the user’s browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

Wait… What is normally used?

Before we dive deep into WebSockets, let’s take a step back and look at HTTP. The HTTP protocol is a set of rules for how computers communicate on the web. It allows for the fetching of resources and is the foundation of any data exchange. It works as a request/response mechanism where a connection is open, a request is sent from the client(eg. the web browser), the server sends a response back and the connection is closed. HTTP is unidirectional, only the client can send a request to the server, and it must wait until the server sends back a separate response for each request. A common example used for this type of communication is a kitchen in a restaurant.

  1. You, as the client place an order (HTTP request) and a waiter takes that order to the back kitchen (aka the server).
  2. The kitchen receives the order and confirms that the order is correct (an item on the menu).
  3. If the kitchen knows how to make it, they will find the ingredients to prepare the order (which is like the server processing the request and fetching data from a database to prepare the response) and will send the food back with the waiter as a successful response.
  4. Say you asked for something not on the menu, well the kitchen might not accept that order or maybe doesn’t have the ingredients to prepare it so the kitchen will send the waiter back with a message that they can’t complete the request. Similarly to the server sending back an error message or status code 404.
HTTP Request/Response Diagram

It is also important to highlight that HTTP is “stateless”, it doesn’t know who specifically the request is coming from (ie. the kitchen doesn’t know who you specifically are when ordering the food). It treats and fulfills every request independently. Because this protocol is unidirectional, the kitchen (server) isn’t able to send the waiter back to you if there is a question they want to ask or update your order. It can only send a response after YOU specifically send the request in. The only way for you to get updated information from the kitchen is if you send the requests through the waiter. In a HTTP protocol the connection is closed after the response has been sent back from the server. This protocol does not lend itself well to real-time applications as a user would have to refresh the page every time they wanted to see an update to the page which, could be quite frustrating.

Before WebSockets were created, several ways that developers would create a work around for this problem were through short polling, long polling or server-sent events (SSE). We won’t get into the nitty gritty details about these but lets just say these solutions were not the most effective for real-time applications and some were not supported by older browsers.

In comes WebSockets! (…finally)

To put it simply, WebSockets are just another protocol for sending and receiving messages. Similar to HTTP, they both send messages over a TCP (Transmission Control Protocol) connection. The only difference is how they structure the messages. WebSockets allow for a bidirectional, real-time communication between clients and servers. Once the connection is established between the client and server, it is kept alive until it is terminated by either party. Messages can be sent back and forth between the the client and the server until one of them dies or decides to close the connection. The WebSockets API allow the server to be able to keep track of each client and push messages to a subset of clients.

How does it work?

Looking at the kitchen example again, if you were using a WebSocket, now you and the kitchen both have an open connection for the duration you are at the restaurant. You can both send requests/responses to each other until someone leaves the connection or terminates it.

To establish a WebSocket connection the client can send an HTTP “hand shake” request with an upgraded header, specifying that the client wants to establish a WebSocket connection. This request is sent to a ws: or wss: URI and if the server is able to establish the connection, it will send a successful response back and the handshake is complete. The TCP/IP connection is left open, allowing bidirectional messages to pass through between each party. This is often referred to as a full-duplex connection.

WebSocket Request/Response Diagram

What are they used for?

As mentioned above, they can be used for real-time web applications. Say you have an interview scheduler app and the calendar being displayed needs to be updated every time a new interview is booked or cancelled. WebSockets provide the perfect solution to this to make sure each user is looking at the most up to date calendar. Another place you might want to use WebSockets is in a multiplayer gaming application where you have a leaderboard that is constantly changing on the server. Alternatively you could also use this for building a chat application. In a chat app, users are constantly sending messages to recipients and the server needs to be able to push these messages to the right recipients and allow for multiple messages to be sent to multiple people at once.

If you want to learn more, I found that this youtube video below by Fireship to be helpful in providing a quick review!

https://www.youtube.com/watch?v=1BfCnjr_Vjg&ab_channel=Fireship

Basic Implementation using ws: a Node.js WebSocket Library

Now let’s set up a simple WebSocket client/server connection using the ws library.

Source: https://github.com/websockets/ws

Server code

First let’s set up the server. We need to require the ws library and then open the connection. Inside the open connection, you can send a on message event to confirm that the connection is opened. Now it will “hang” and wait like an event listener for events and can send a message back if need be.

const WebSocket = require("ws");
const wss = new WebSocket.Server({ server });
//open the connectionwss.on("connection", socket => {
socket.onmessage = event => {
console.log(`message received: ${event.data}`);
//if the client sends an event with the word "ping" the server will send back the response "pong" if (event.data === "ping") {
socket.send(JSON.stringify("pong"));
}
};
});

Client code

On the client side, the WebSocket is opened and immediately sends a message to the server. On the event that they receive a message back, they print out the response they received.

//the WebSocket constructor accepts one required and one optional parameter. ==> (url, protocols)const socket = new WebSocket(ws://localhost:8080);socket.onopen = () => {
socket.send("ping");
socket.onmessage = (event) => {
console.log(`this event: ${event.data}`);
};
};

After setting up the client and server code you will now be able to establish a WebSocket connection! As you can see in the photos below (taken from the browser DevTools Network tab), the connection has been upgraded to a WebSocket connection. You can also see that the client sends the string “ping” to the server and the server sends back the string “pong”.

Upgrade to WebSocket Protocol
Messages from Client/Server

What about Socket.IO?

Now that we have a basic understanding of WebSockets we can start to look at the many libraries that can help build these connections easier. Socket.IO is known as one of the more popular ones. As stated on their website Socket.IO…

…is a library that enables real-time, bidirectional and event-based communication between the browser and the server. It consists of a Node.js server and a javascript client library for the browser.

One of the challenges with the previous ws example is that the server cannot broadcast a message to multiple clients simultaneously. For example, say you want to build a group chat room with multiple messages being sent all at once, you need a way to update all of the users with the most recent messages. It can definitely be done with WebSockets but there are libraries like Socket.IO that can make features easier to implement.

How to use Socket.IO?

The documentation explains that the Socket.IO library is a “slight wrapper” around the WebSocket API. It is also important to note that they are not a WebSocket implementation and to use something like the ws library if that is what you are after in your application.

As shown in their documentation, it can be quite easy to set up. (See below for an excerpt from their online docs)

Client code

const socket = io("ws://localhost:3000");

socket.on("connect", () => {
// either with send()
socket.send("Hello!");

// or with emit() and custom event names
socket.emit("salutations", "Hello!", { "mr": "john" }, Uint8Array.from([1, 2, 3, 4]));
});

// handle the event sent with socket.send()
socket.on("message", data => {
console.log(data);
});

// handle the event sent with socket.emit()
socket.on("greetings", (elem1, elem2, elem3) => {
console.log(elem1, elem2, elem3);
});

Server Code

const io = require("socket.io")(3000);

io.on("connection", socket => {
// either with send()
socket.send("Hello!");

// or with emit() and custom event names
socket.emit("greetings", "Hey!", { "ms": "jane" }, Buffer.from([4, 3, 3, 1]));

// handle the event sent with socket.send()
socket.on("message", (data) => {
console.log(data);
});

// handle the event sent with socket.emit()
socket.on("salutations", (elem1, elem2, elem3) => {
console.log(elem1, elem2, elem3);
});
});

One of the benefits of using Socket.IO is that it offers additional features over a plain WebSocket object. It has reliability (uses HTTP long polling if the WebSocket connection cannot be established), automatic reconnection, packet buffering, acknowledgements, broadcasting to all clients or a subset of clients (eg. rooms) and multiplexing (aka Namespace).

What about Pusher?

Now that we have talked about a couple of libraries, and are a little more comfortable with WebSockets. A thought has come across your mind, what if I have to deal with a database? Well, using a paid service could potentially help prevent a lot of headache. You might have stumbled upon the Pusher API, a hosted service that claims they make it VERY easy to add real-time data and functionality to your web and mobile applications. Pusher acts as a real-time layer between your severs and clients and maintains persistent connections to the clients over WebSocket. Similarly to Socket.IO, if the WebSocket connection cannot be established it will fall back to an HTTP based connectivity. Pusher states that they handle the complex infrastructure so you as a developer can focus on the experience.

Wow, I am on the verge on sounding like an infomercial… But in all seriousness. Why would you want to pay to use Pusher compared to having your own servers? From browsing their website and dabbling in a free tutorial that they offer (building a small real-time polling app) based on Node.js and React, it seems like they have a lot to offer. Pusher is useful in providing services such as push notifications, in-application chat clients, real-time location tracking and even real-time updated data charts/dashboards. They take the challenging part out of building the backend infrastructure to support WebSockets to give you back valuable time to solve other problems or bugs that may arise.

So after all that, maybe you could say that a WebSocket is similar to being a new developer. Always having an open, persistent connection to learning new things? ;) Well on that note, I hope this guide helped you learn a little more about something you maybe didn’t know a lot about and you will now have a few more tools in your tool box for building real-time web applications.

References:

https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

https://developer.mozilla.org/en-US/docs/Web/HTTP

https://stackoverflow.blog/2019/12/18/websockets-for-fun-and-profit/

--

--