In the current digital landscape, real-time features have become crucial for modern web applications. Real-time features allow applications to provide instantaneous feedback, live updates, and continuous data streams, creating a more dynamic and interactive user experience. There are many technologies available to implement real-time functionality in web applications. Each technology has its own strengths. Today, I will introduce you to one of the most popular and flexible options. It is Socket.IO.
 

What is Socket.IO?

Socket.IO is a JavaScript library that enables real-time, bidirectional, and event-based communication between web clients and servers. It simplifies the complexities of real-time transport protocols, providing a unified API that makes it straightforward to implement real-time features in web applications.
 

The features provided by Socket.IO

Socket.IO is a powerful library for implementing real-time, bidirectional communication in web applications. Here are some of the key features provided by Socket.IO:
  1. Real-Time Communication:
    Socket.IO enables real-time, low-latency communication between clients and servers, allowing for instant data exchange.
  2. Bidirectional Data Flow:
    Both clients and servers can send and receive data simultaneously, enabling interactive and responsive applications.
  3. Event-Driven Architecture:
    Socket.IO uses an event-driven model, where clients and servers can emit and listen for events. This simplifies the handling of real-time interactions.
  4. Cross-Platform Compatibility:
    Socket.IO is compatible with multiple platforms, including web browsers, Node.js servers, and mobile devices. This ensures consistent real-time communication across different environments.
  5. Automatic reconnection:
    Socket.IO automatically handles connection loss and attempts to reconnect the client to the server in the event of lost connection or network problems. This ensures a stable and reliable communication channel.
  6. Scalability:
    Socket.IO supports namespaces and rooms, allowing you to organize and scale real-time communication efficiently. A Namespace is a communication channel that allows you to split the logic of your application over a single shared connection, while rooms enable broadcasting messages to specific groups of clients.
  7. Broadcasting:
    Socket.IO allows you to broadcast messages to all connected clients or to specific groups of clients (rooms). This enables scenarios like sending chat messages to all users in a chat room or notifying specific users about relevant events.

How does Socket.IO work?

Socket.IO works by establishing a bidirectional communication channel between a client (such as a web browser) and a server (typically implemented using Node.js). This communication channel enables real-time data exchange, allowing both the client and server to send and receive messages instantly.
 
In Socket.IO, sending and receiving messages involves emitting and listening for events. Here's how you can send and receive messages using Socket.IO:
  1. Sending Messages (Emitting Events):
    To send a message from the server to the client, you use the emit() method provided by the Socket.IO server library within the context of a connection event. You need to specify the name of the event you want to emit.
    io.on('connection', (socket) => {
        // Sending a 'new message' event to the client
       socket.emit('new message', 'Welcome to the chat!');
    });
     
  2. Receiving Messages (Listening for Events):
    To receive a message on the client, you listen for the event emitted by the server using the on() method provided by the Socket.IO client library.
    // Listening for the 'new message' event on the client
    socket.on('new message', (msg) => {
        console.log('New message received:', msg);
    });
     
  3. Broadcasting events:
    Broadcasting events in Socket.IO allows a server to send a message to multiple clients simultaneously. Socket.IO provides several methods for broadcasting events, allowing developers to target specific groups of clients or all connected clients.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on(new message', (msg) => {
            // Broadcast to all clients except the sender
            socket.broadcast.emit(new message', msg);
        });
    });
    In this example, when a client sends a ‘new message' event, the server broadcasts that message to all connected clients except the one that sent the message.
     
  4. Broadcasting to Specific Rooms:
    Socket.IO supports the concept of rooms, which allow you to broadcast messages to a subset of clients. A room is simply a named channel that sockets can join and leave.
    Joining a room: Clients can join a room using the join() method.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on('join room', (roomName) => {
            socket.join(roomName);
        });
    });
     
    Broadcasting to a Room: To broadcast an event to all clients in a specific room, use the to() or in() method.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on(new message', (roomName, msg) => {
            // Broadcast to all clients in the specified room
            io.to(roomName).emit(new message', msg);

            // Broadcast to all clients in room1 and room2
            io.to('room1').to('room2').emit('chat message', msg);
        });
    });

Building a real-time chat application with Socket.IO

Description: Building a real-time Chat application where users can join rooms, send, and receive messages within those rooms using Vue 3, Express, and Socket.io.
Server Setup (Node.js):
  1. Initialize a new project:
    mkdir socketio-chat
    cd socketio-chat
    npm init -y
  2. Install Express and Socket.IO:
    npm install express socket.io
  3. Create the server file (index.js):
    const express = require('express');
    const http = require('http');
    const socketIo = require('socket.io');

    const app = express();
    const server = http.createServer(app);
    const io = socketIo(server);
    app.use(express.static('public'));

    let socketsConected = new Set()
    io.on('connection', (socket) => {
        console.log('Socket connected', socket.id)
        socketsConected.add(socket.id)
        // Emit 'clients-total' event to clients to display numbers user in the room
        io.emit('clients-total', socketsConected.size)

        socket.on('disconnect', () => {
            console.log('Socket disconnected', socket.id)
            socketsConected.delete(socket.id)
            io.emit('clients-total', socketsConected.size)
        })

        socket.on('joinRoom', (room) => {
            // Subscribe the socket to a given channel
            socket.join(room);
            socket.room = room;
        });

        socket.on('chat-message', (data) => {
            data.sender = socket.id
            // Broadcast to all clients in the specified room
            io.to(socket.room).emit('chat-message', data)
        })
    })
    server.listen(4000, () => console.log(`💬 server on port 4000`))
    Explanation: The server is listening for two main events 'joinRoom' and 'chat-message'. The ‘joinRoom’ event will subscribe the socket to a given channel (The room name user selected). The ‘chat-message’ event will listen for the ‘chat-message’ event sent from the client, and then it will send the data to all clients in the specified room via the ‘chat-message’ event.
Client Setup (Vue 3 + Socket.io-client)
Create an HTML file (index.html):
 
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>App Chat</title>
    </head>
    <body>
        <div id="app" style="width: 416px;">
            <h1 class="title">App Chat 💬</h1>
            <!-- Join room -->
            <div v-if="!joinedRoom" class="join-room d-flex flex-column">
                <select v-model="selectedRoom" class="form-select">
                    <option v-for="room in rooms" :key="room" :value="room">{{ room }}</option>
                </select>
                <input v-model="nameInput" type="text" class="form-control mt-1 mb-1" placeholder="Please enter name">
                <button type="submit" class="btn w-25 btn-light" @click="joinRoom()">Join Room</button>
            </div>
            <!-- Box chat -->
            <div v-else class="main">
                <div class="name">
                    <span class="name-input w-75">{{ selectedRoom }}</span>
                    <span class="name-input w-25 text-end">{{ clientsTotal }} <i class="far fa-user"></i></span>
                </div>
                <ul class="message-container" id="message-container" >
                </ul>
                <form class="message-form" id="message-form">
                    <inputtype="text"
                        v-model="messageInput"
                        class="message-input"
                        ref="messageInputEl"
                    />
                    <div class="v-divider"></div>
                    <button type="submit" class="send-button" @click="sendMessage($event)">
                        send <span><i class="fas fa-paper-plane"></i></span>
                    </button>
                </form>
            </div>
        </div>
   
        <!-- Include Vue 3 from CDN -->
        <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
        const {
            createApp,
            ref,
            onMounted
        } = Vue;
   
        createApp({
            setup() {
                const socket = io()
                const messageInput = ref('');
                const clientsTotal = ref(0)
                const nameInput = ref('')
                const selectedRoom = ref(null)
                const joinedRoom = ref(false)
                const socketId = ref(null)
                const rooms = ref(['Room 1', 'Room 2', 'Room 3'])
                onMounted(() => {
                    socket.on('connect', () => {
                        socketId.value = socket.id;
                    });
                });
   
                const sendMessage = async (e) => {
                    e.preventDefault()
                    if (messageInput.value === '') return
                    const data = {
                        name: nameInput.value,
                        message: messageInput.value,
                        dateTime: new Date(),
                    }
                    socket.emit('chat-message', data)
                    addMessageToUI(true, data)
                    messageInput.value = ''
                }
   
                const addMessageToUI = (isOwnMessage, data) => {
                    const element = `
                        <li class="${isOwnMessage ? 'message-right' : 'message-left'}">
                            <p class="message">
                                ${data.message}
                                <span>${data.name} ● ${moment(data.dateTime).fromNow()}</span>
                            </p>
                        </li>`;
                    const messageContainer = document.getElementById('message-container')
                    messageContainer.innerHTML += element
                }
   
                socket.on('chat-message', (data) => {
                    if (socketId.value != data.sender) {
                        addMessageToUI(false, data)
                    }
                })
                socket.on('clients-total', (value) => {
                    clientsTotal.value = value
                })
   
                const joinRoom = () => {
                    joinedRoom.value = true
                    socket.emit('joinRoom', selectedRoom.value);
                }
   
                return {
                    messageInput,clientsTotal, rooms,
                    selectedRoom,joinedRoom,nameInput,
                    sendMessage, joinRoom
                };
            }
        }).mount('#app');
        </script>
    </body>
    </html>

 

Explanation:

  • Vue 3 Setup: Use Vue 3 from the CDN to create a simple Vue application.
  • Socket.io-client: Include Socket.io-client from the CDN and establish a connection to the server.
  • Event Handling: Listen for 'chat-message' and 'clients-total' events from the server to update the message list and number of users in the room. Emit 'joinRoom' and 'chat-message' events to the server.
  • Joining Room: Allow users to join a room by emitting a 'joinRoom' event to the server.
  • Sending Messages: Allow users to send messages to the room by emitting a 'chat-message' event to the server when the Enter key is pressed.
 

Demo:

Image 1

 

Image 2

 

Conclusion

Socket.IO offers numerous advantages that make it an excellent choice for implementing real-time features in web applications. Its real-time communication capabilities, bidirectional data flow, event-driven architecture, and cross-platform compatibility provide a robust foundation for building interactive and responsive applications. Additionally, features like automatic reconnection, transport layer abstraction, scalability, and a rich ecosystem further enhance its appeal. By leveraging Socket.IO, developers can create engaging and highly responsive web applications that meet the needs of today's users.
 

References

 

 

 

Leave a comment

*