This article explores Socket.IO, its main use cases, and how to get started. The content is suitable primarily for people who are new to Socket.IO.
On the other hand, if you are already familiar with Socket.IO and pondering whether to integrate it into your tech stack, have a read of our other blog post: Scaling Socket.IO - practical considerations.
What is Socket.IO?
Socket.IO was created in 2010. It was developed to use open connections to facilitate realtime communication, still a relatively new phenomenon at the time.
Socket.IO allows bi-directional communication between client and server. Bi-directional communications are enabled when a client has Socket.IO in the browser, and a server has also integrated the Socket.IO package. While data can be sent in a number of forms, JSON is the simplest.
To establish the connection, and to exchange data between client and server, Socket.IO uses Engine.IO. This is a lower-level implementation used under the hood. Engine.IO is used for the server implementation and Engine.IO-client is used for the client.
Socket.IO brings to mind WebSockets. WebSockets are also a browser implementation allowing bi-directional communication, however, Socket.IO does not use this as standard. First, Socket.IO creates a long-polling connection using xhr-polling. Then, once this is established, it upgrades to the best connection method available. In most cases, this will result in a WebSocket connection. See how WebSockets fare against long-polling (and why WebSockets are nearly always the better choice).
How does Socket.IO work?
A popular way to demonstrate the two-way communication Socket.IO provides is a basic chat app (we talk about some other use cases below). With sockets, when the server receives a new message it will send it to the client and notify them, bypassing the need to send requests between client and server. A simple chat application shows how this works.
Example – Socket.IO for chat
Server
You will need to have node.js installed. We will be using express to simplify setup.
Create a new folder with:
$ mkdir socket.io-example
cd socket.io-example
npm install socket.io express
Setup server and import required packages.
const app = require("express")();
const http = require("http").createServer(app);
const io = require("socket.io")(http);
The server root will send our index.html
which we will setup shortly.
app.get("/", (req, res) => res.sendFile(__dirname + "/index.html"));
Here is where we setup Socket.IO. It is listening for a ‘connection’ event and will run the provided function anytime this happens.
io.on("connection", function(socket) {
console.log(“socket connected”);
});
This will setup the server to listen on port 3000.
http.listen(3000, () => console.log("listening on http://localhost:3000")
Run the application with node
index.js
and open the page in your browser.
Client
Include the following scripts on your page, before the closing </body>
tag. You now have a socket connection setup.
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
</script>
This is the minimum setup to get the Socket.IO connection working. Let’s go a bit further to get messages sent back and forth.
Server
Inside the function we are using io.emit()
to send a message to all the connected clients. This code will notify when a user connects to the server.
io.on("connection", function(socket) {
io.emit(“user connected”);
});
If you want to broadcast to everyone except the person who connected you can usesocket.broadcast.emit()
.
We will also add a listener for any new messages received from a client and send a message to all users in response.
io.on("connection", function(socket) {
io.emit(“user connected”);
socket.on(“message", function(msg) {
io.emit("message", msg);
});
});
How to add these events into the client is shown below.
Client
Here is an index.html
file which includes our previous scripts, a simple form with input for new messages and a container for displaying messages
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Socket.io Example</title>
</head>
<body>
<h1>Our Socket.io Chat Application</h1>
<div>
<h2>Messages</h2>
<ul></ul>
</div>
<form action="">
<input type="text" />
<button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
</script>
</body>
</html>
Now we will add some additional logic to our <script>
.
<script>
// select relevant elements
const form = document.querySelector("form");
const input = document.querySelector("input");
messageList = document.querySelector("ul");
// establish socket.io connection
const socket = io();
// handle sending message to server & input reset
function sendMessage(e) {
// prevent form submission refreshing page
e.preventDefault();
// send input value to server as type 'message'
socket.emit("message", input.value);
// reset input value
input.value = "";
}
// add listener to form submission
form.addEventListener("submit", sendMessage);
// add message to our page
function addMessageToHTML(message) {
// create a new li element
const li = document.createElement("li");
// add message to the elements text
li.innerText = message;
// add to list of messages
messageList.append(li);
}
// watch for socket to emit a 'message'
socket.on("message", addMessageToHTML);
// display message when a user connects
function alertUserConnected() {
addMessageToHTML("User connected");
}
// watch for socket to emit a 'user connected' event
socket.on("user connected", alertUserConnected);
</script>
The key points here are the socket.on(event, callback)
functions. When our server emits events which match the first ‘event’ argument the callback will be run. Inside these callbacks we can take the actions we want on the client-side. In this case, displaying the message on the screen.
Maintaining & Operating Socket.IO
As explained above, getting started with Socket.IO is relatively simple – all you need is a Node.js server to run it on. If you want to get started with a realtime app for a limited number of users, Socket.IO is a good option. Problems come when working at scale. Say, for example, you want to build a CRM-like app that enables communications between businesses. Socket.IO is built on asynchronous networking libraries and will cause load on your server. Maintaining connections to users as well as sending and receiving messages adds strain, and if clients start sending significant amounts of data via Socket.IO, it streams data in chunks, freeing up resources when the data chunk is transmitted. So when your application attracts more users and your server reaches its maximum load you will need to split connections over multiple servers, or risk losing important information.
Unfortunately this is not as simple as adding another server. Sockets are an open connection between a server and client. The server only knows about the clients who have connected directly with it and not those connected to other servers. Going back to the conversation function, imagine you want to broadcast a message to all users that someone joined the chat. If they are connected to a different server they wouldn’t receive this message.
To solve this problem you need to have a pub/sub store (e.g. Redis). This store will solve the aforementioned problem by notifying all the servers that they need to send the message when someone joins the chat. Unfortunately, this means an additional database to maintain which will most likely require its own server.
Socket.IO have created a Socket.io adapter which works with the pub/sub store and servers to share information. You can write your own implementation of this adapter or you can use the one they have provided for Redis, with which, luckily, Socket.IO is easy to integrate.
Other reliability enhancers for Socket.IO might include CoreOS to break down architecture into units that can be distributed across available hardware, introducing new instances as the load increases.
Another issue with scaling Socket.IO is that whilst WebSockets hold their connection open, if the connection falls back to polling then there are multiple requests during the connection lifetime. When one of these requests goes to a different server you will receive an error `Error during WebSocket handshake: Unexpected response code: 400`.
The two main ways to solve this are by routing clients based on their originating address, or a cookie. Socket.IO has great documentation on how to solve this for different environments.
While Socket.IO does tend to have good documentation for ways round its limitations, these generally count as ‘remedies’ rather than solutions. If you intend to scale further, these suggested ways round end up adding complexity and extra margin for error to your stack.
When does Socket.IO reach its limits?
As with all tech, choosing the right one means being clear on your ambitions for your product. Socket.IO does make many things easier in comparison to setting up sockets yourself, but there are limitations and drawbacks in addition to the scaling issue mentioned above.
The first is that the initial connection is longer compared to WebSockets. This is due to it first establishing a connection using long polling and xhr-polling, and then upgrading to WebSockets if available.
If you don’t need to support older browsers and aren’t worried about client environments which don’t support WebSockets you may not want the added overhead of Socket.IO. You can minimise this impact by specifying to only connect with WebSockets. This will change the initial connection to WebSocket, but remove any fallback.
Realtime you can depend on. Ably allows you to easily build complete, simplified realtime applications that scale. Try our APIs for free.
Client
Const socket = io({transports: [“websocket”], upgrade: false});
Server
io.set("transports", ["websocket"]);
In this scenario, the client will still need to download the 61.2 KB socket.io
JavaScript file. This file is 61.2 KB. More information on this process is here.
For streaming that’s data heavy by definition, for example video streaming, sockets are not the answer. If you want to support data exchange on this level a better solution is webRTC or a data-streaming as a service provider, Ably being one of several.
Socket.IO – the future?
On 9th March 2021, Socket.IO V4 was released. It is an API clean-up with several new features like the much-awaited support for Typed Events, Immutability, and several bug fixes.
Looking at NPM downloads, Socket.IO use has been increasing but only gradually.
On the other hand, Sockjs and WS have been steadily growing and have outpaced Socket.IO in NPM downloads.
This indicates that although use of sockets has increased, developers have chosen alternatives to Socket.IO. Some have chosen packages such as WS or SockJS. Others have opted for a hosted solutions where the complexity of real-time messages is handled for you, and many of whom operate freemium models.
As you can see in "Can I use", all modern browsers now support WebSockets. This negates some of the need for a package which handles socket connections on the browser and explains the rise in popularity of packages such as WS which handle the server-side socket connection, but relies on the native browser API for client-side connections and communication.
As we have explored, Socket.IO is a great tool for developers wanting to set up bi-directional socket connections between client and server. This makes simple applications such as live chat much simpler to implement. Socket.IO makes many things easier and provides fallbacks for unsupported clients, but has its own trade-offs.
Scaling applications is perhaps the most difficult step in using sockets, and Socket.IO’s implementation for non-WebSocket connections further complicates the process. Socket.IO’s future support is also questionable.
Aside from the question of future support, whether or not to use socket.io really depends on individual use case – for starting out building simple realtime applications, socket.io works well. With WebSocket support widely spread (answering to a huge growth in demand for realtime applications and services since Socket.IO was set up in 2010), there is now more choice to use similar packages closer to the native implementation, so it’s worth comparing Socket.IO to these as well. For more complex apps, or apps you think will scale, be prepared to add other tech to your stack. To help gauge what stage your app is at in terms of future scale, realtime needs, get in touch with Ably’s realtime engineers. We aim to be as impartial as possible.
About Ably
Ably Realtime is an enterprise-ready pub/sub messaging platform. We make it easy to efficiently design, quickly ship, and seamlessly scale critical realtime functionality delivered directly to end-users. Everyday we deliver billions of realtime messages to millions of users for thousands of companies.
We power the apps people, organizations, and enterprises depend on everyday like Lightspeed System’s realtime device management platform for over seven million school-owned devices, Vitac’s live captioning for 100s of millions of multilingual viewers for events like the Olympic Games, and Split’s realtime feature flagging for one trillion feature flags per month.
We’re the only pub/sub platform with a suite of baked-in services to build complete realtime functionality such as showing a driver’s live GPS location on a home-delivery app, instantly loading the most recent score when opening a sports app, and automatically handling reconnection when swapping networks.
Our platform is mathematically modelled around Four Pillars of Dependability so we’re able to ensure messages don’t get lost while still being delivered at low latency over a secure, reliable, and highly available global edge network.
Try Ably and see why developers from startups to industrial giants choose to build on our platform to simplify engineering, minimize DevOps overhead, and increase development velocity.
Further reading
Recommended Articles
Scaling Socket.IO - practical considerations
A review of Socket.IO’s advantages, limitations & performance. Learn about the challenges of using Socket.IO to deliver realtime apps at scale.
SignalR vs. Socket.IO: which should you choose in 2024?
Discover the differences between SignalR and Socket.IO, their pros and cons, and use cases to see which is right for you.