Introducing Socket.io

Socket.io

Web applications, for example, Facebook, Twitter, or Gmail are consolidating constant abilities, which empower the application to consistently give the client as of late refreshed data.

When you include the Socket.io module, it provides you with two objects: a socket server object that is responsible for the server functionality and a socket client object that handles the browser’s functionality.

The Socket.io server object

The Socket.io server object is where it all begins. require(‘Socket.io’) module, and then create a new Socket.io server instance that will interact with socket clients. The server object supports both a standalone implementation and the ability to use it in conjunction with the Express framework. The server instance then exposes a set of methods that allow you to manage the Socket.io server operations. Once the server object is initialized, it will also be responsible for serving the socket client JavaScript file for the browser. A Standalone Socket.io server will look as follows:

var io = require('socket.io')();
io.on('connection', function(socket){ /* ... */ });
io.listen(3000);

This will open a Socket.io over the 3000 port and serve the socket client had the file on at the URL http://localhost:3000/socket.io/socket.io.js. Implementing Socket.io server in conjunction with an Express application will be a bit different:

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket){ /* ... */ });
server.listen(3000);

This time, you first use the HTTP module of Node.js to create a server and wrap the Express application. The server object is then passed to the Socket.io module and serves both the Express application and the Socket.io server. Once the server is running, it will be available for socket clients to connect. A client trying to establish a connection with the Socket.io server will start by initiating the handshaking process.

Socket.io handshaking

When a client wants to connect the Socket.io server, it will first send a handshake HTTP request. The server will then analyze the request to gather the necessary information for ongoing communication. It will then look for configuration middleware that is registered with the server and execute it before firing the connection event. When the client is connected to the server, the connection event listener is executed, exposing a new socket instance. Once the handshaking process is over, the client is connected to the server and all communication with it is handled through the socket instance object.

For example, handling a client’s disconnection event will be as follows:

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket){
  socket.on('disconnect', function() {
    console.log('user has disconnected');
  });
});
server.listen(3000);

Notice how the socket.on() method adds an event handler to the disconnection event. Although the disconnection event is a predefined event, this approach works the same for custom events as well, as you will see in the following sections. While the handshake mechanism is fully automatic, Socket.io does provide you with a way to intercept the handshake process using a configuration middleware.

The Socket.io configuration middleware
var app = require('express')(); 
var server = require('http').Server(app); 
var io = require('socket.io')(server); 
io.use(function(socket, next) { 
/* ... */
next(null, true); 
}); 
io.on('connection', function(socket){ 
   socket.on('disconnect', function() { 
     console.log('user has disconnected'); 
   });
});
server.listen(3000);

As you can see, the io.use() method callback accepts two arguments: the socket object and a next callback. The socket object is that will be used for the connection and it holds some connection properties. One property is the socket.request property, which represents the handshake HTTP request.

In the following sections, you will use the handshake request to incorporate the Passport session with the Socket.io connection. The next argument is a callback method that accepts two arguments: an error object and Boolean value. The next callback tells Socket.io whether or not to proceed with the handshake process, so if you pass an error object or a false value to the next method, Socket.io will not initiate the socket connection. Now that you have a basic understanding of how handshaking works, it is time to discuss the Socket.io client object.

The Socket.io client object

The Socket.io client object is responsible for the implementation of the browser socket communication with the Socket.io server. You start by including the Socket.io client JavaScript file, which is served by the Socket.io server. The Socket.io exposes an io() method that connects to the Socket.io server and creates the client socket object. A simple implementation of the socket client will be as follows:

<script src="/socket.io/socket.io.js"></script> 
<script>
var socket = io(); 
socket.on('connect', function() {
    /* ... */ 
});
</script>

Notice the default URL for the Socket.io client object. Although this can be altered, you can usually leave it like this and just include the file from the default Socket.io path. Another thing the io() method will automatically try to connect to the default base path when executed with no arguments; however, you can also pass a different server URL as an argument. As you can see, the socket client is much easier to implement, so we can move on to discuss how Socket.io handles real-time communication using events.

Socket.io events

To handle the communication between the client and the server, Socket.io uses a structure that mimics the WebSockets protocol and fires events messages across the server and client objects. There are two types of events: system events, which indicate the socket connection status, and custom events, which you’ll use to implement your business logic.

The system events on the socket server are as follows:
• io.on(‘connection’, …): This is emitted when a new socket is connected.
• socket.on(‘message’, …): This is emitted when a message is sent using the socket.send() method.
• socket.on(‘disconnect’, …): This is emitted when the socket is disconnected.
The system events on the client are as follows:
• socket.io.on(‘open’, …): This is emitted when the socket client opens a connection with the server.
• socket.io.on(‘connect’, …): This is emitted when the socket client is connected to the server.
• socket.io.on(‘connect_timeout’, …): This is emitted when the socket client connection to the server is timed out.
• socket.io.on(‘connect_error’, …): This is emitted when the socket client fails to connect to the server.
• socket.io.on(‘reconnect_attempt’, …): This is emitted when the socket client tries to reconnect to the server.
• socket.io.on(‘reconnect’, …): This is emitted when the socket client is reconnected to the server.
Adding Real-time Functionality Using Socket.io
• socket.io.on(‘reconnect_error’, …): This is emitted when the socket client fails to reconnect to the server.
• socket.io.on(‘reconnect_failed’, …): This is emitted when the socket client fails to reconnect to the server.
• socket.io.on(‘close’, …): This is emitted when the socket client closes the connection with the server.

Handling events

While system events are helping us with connection management, the real magic of Socket.io relies on using custom events. In order to do so, Socket.io exposes two methods, both on the client and server objects. The first method is the on() method, which binds event handlers with events and the second method is the emit() method, which is used to fire events between the server and client objects. An implementation of the on() method on the socket server is very simple:

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket){
 socket.on('customEvent', function(customEventData) {
 /* ... */
 });
});
server.listen(3000);

In the preceding code, you bound an event listener to the customEvent event. The event handler is being called when the socket client object emits the customEvent event. Notice how the event handler accepts the customEventData argument that is passed to the event handler from the socket client object. An implementation of the on() method on the socket client is also straightforward:

var socket = io();
socket.on('customEvent', function(customEventData) {
/* ... */
});

This time the event handler is being called when the socket server emits the customEvent event that sends customEventData to the socket client event handler. Once you set your event handlers, you can use the emit().

Emitting events

On the socket server, the emit() method is used to send events to a single socket client or a group of connected socket clients. The emit() method can be called from the connected socket object, which will send the event to a single socket client, as follows:

io.on('connection', function(socket){
socket.emit('customEvent', customEventData);
});

The emit() method can also be called from the io object, which will send the event to all connected socket clients, as follows:

io.on('connection', function(socket){
io.emit('customEvent', customEventData);
});

Another option is to send the athletic championship to bodily connected socket clients protest from the sender via the word property, as shown in the following lines of code:

io.on('connection', function(socket){
socket.broadcast.emit('customEvent', customEventData);
});

On the socket client, things are much simpler. Since the socket client is only connected to the socket server, the emit() method will only send the event to the socket server:

var socket = io();
socket.emit('customEvent', customEventData);

Although these methods allow you to switch between personal and global events, they still lack the ability to send events to a group of connected socket clients. Socket.io offers two options to group sockets together: namespaces and rooms.

Socket.io namespaces

In order to easily control socket management, Socket.io enables developers to split socket connections according to their purpose using namespaces. So instead of creating different socket servers for different connections, you can just use the same server to create different connection endpoints. This means that socket communication can be divided into groups, which will then be handled separately.

Socket.io server namespaces

To create a socket server namespace, you will need to use the socket server of() method that returns a socket namespace. Once you retain the socket namespace, you can just use it the same way you use the socket server object:

const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.of('/someNamespace').on('connection', (socket) => {
 socket.on('customEvent', (customEventData) => {
 /* ... */
 });
});
io.of('/someOtherNamespace').on('connection', (socket) => {
 socket.on('customEvent', (customEventData) => {
 /* ... */
 });
});
server.listen(3000);

In fact, when you use the io object, Socket.io actually uses a default empty
namespace, as follows:

io.on('connection', (socket) => {
/* ... */
});

The preceding lines of code are actually equivalent to this:

io.of('').on('connection', (socket) => {
/* ... */
});
Socket.io client namespaces

On the socket client, the implementation is a little different:

var someSocket = io('/someNamespace');
someSocket.on('customEvent', function(customEventData) {
/* ... */
});

var someOtherSocket = io('/someOtherNamespace');
someOtherSocket.on('customEvent', function(customEventData) {
/* ... */
});

As you can see, you can use multiple namespaces on the same application without much effort. However, once sockets are connected to different namespaces, you will not be able to send an event to all these namespaces at once. This means that
namespaces are not very good for a more dynamic grouping logic. For this purpose, Socket.io offers a different feature called rooms.

Socket.io rooms

Socket.io rooms allow you to partition connected sockets into different groups in a dynamic way. Connected sockets can join and leave rooms, and Socket.io provides you with a clean interface to manage rooms and emit events to the subset of sockets in a room. The rooms functionality is handled solely on the socket server but can easily be exposed to the socket client.

Joining and leaving rooms

Joining a room is handled using the socket join() method, while leaving a room is handled using the leave() method. So, a simple subscription mechanism can be implemented as follows:

io.on('connection', (socket) => {
 socket.on('join', (roomData) => {
 socket.join(roomData.roomName);
 })
 socket.on('leave', (roomData) => {
 socket.leave(roomData.roomName);
 })
});

Note that the join() and leave() methods both take the room name as the first argument


Posted

in

by

Recent Post

  • How to Implement In-Order, Pre-Order, and Post-Order Tree Traversal in Python?

    Tree traversal is an essential operation in many tree-based data structures. In binary trees, the most common traversal methods are in-order traversal, pre-order traversal, and post-order traversal. Understanding these tree traversal techniques is crucial for tasks such as tree searching, tree printing, and more complex operations like tree serialization. In this detailed guide, we will […]

  • Mastering Merge Sort: A Comprehensive Guide to Efficient Sorting

    Are you eager to enhance your coding skills by mastering one of the most efficient sorting algorithms? If so, delve into the world of merge sort in Python. Known for its powerful divide-and-conquer strategy, merge sort is indispensable for efficiently handling large datasets with precision. In this detailed guide, we’ll walk you through the complete […]

  • Optimizing Chatbot Performance: KPIs to Track Chatbot Accuracy

    In today’s digital age, chatbots have become integral to customer service, sales, and user engagement strategies. They offer quick responses, round-the-clock availability, and the ability to handle multiple users simultaneously. However, the effectiveness of a chatbot hinges on its accuracy and conversational abilities. Therefore, it is necessary to ensure your chatbot performs optimally, tracking and […]

  • Reinforcement Learning: From Q-Learning to Deep Q-Networks

    In the ever-evolving field of artificial intelligence (AI), Reinforcement Learning (RL) stands as a pioneering technique enabling agents (entities or software algorithms) to learn from interactions with an environment. Unlike traditional machine learning methods reliant on labeled datasets, RL focuses on an agent’s ability to make decisions through trial and error, aiming to optimize its […]

  • Understanding AI Predictions with LIME and SHAP- Explainable AI Techniques

    As artificial intelligence (AI) systems become increasingly complex and pervasive in decision-making processes, the need for explainability and interpretability in AI models has grown significantly. This blog provides a comprehensive review of two prominent techniques for explainable AI: Local Interpretable Model-agnostic Explanations (LIME) and Shapley Additive Explanations (SHAP). These techniques enhance transparency and accountability by […]

  • Building and Deploying a Custom Machine Learning Model: A Comprehensive Guide

    Machine Learning models are algorithms or computational models that act as powerful tools. Simply put, a Machine Learning model is used to automate repetitive tasks, identify patterns, and derive actionable insights from large datasets. Due to these hyper-advanced capabilities of Machine Learning models, it has been widely adopted by industries such as finance and healthcare.  […]

Click to Copy