Socket.io

First, let's understand WebSocket and then move to Socket.io and implement it in the project.🎊

🍁What is WebSocket?

WebSocket is a computer communication protocol, that provides a full-duplex communication channel over a single TCP connection.

WebSocket provides real-time communication between the client and the web server. Lets' understand what it means when you are trying to get your favorite website you usually start by typing the address in the browser.

The browser then sends the request to the server (a virtual place where information is stored). The server then checks if the requested website exists, and sends the response back to the browser. If the page exists, the browser sends the response. If not then the browser shows the error.

🍁HTTP and Websocket

When the browser sent a request to the server to get our blog.sagarmedtiya.me page, it used a protocol known as HTTP(Hypertext Transfer Protocol), which defines how messages are formatted and transmitted and what actions web servers and browsers should take in response to various commands.

The main principle of HTTP is, to get information from the webpage, the browser first has to ask the server if this information exists. The server then goes to check and sends a response.

This is where WebSockets shines. If you want to get updates in real-time, WebSockets allows a persistent connection between your browser and the server and typically runs over TCP/IP (Transmission Control Protocol/Internet Protocol). WebSockets uses socket -- continuous channels of communication between two ends. Whenever the server receives new information, it automatically sends it to the client through the socket.

Now if you've fully understood the WebSockets. We can go ahead and use Socket.io to make a Realtime Chat application.

🍁Socket.io

Sockets.io been the solution around which most real-time systems are architected, providing a bi-directional communication channel between a client and a server. This means whenever an event occurs, the server can push messages to clients.

Socket.io scheme can be implemented in any application that relies on event-driven, real-time data streams over WebSocket, Comet-style, or peer-to-peer connections. This includes instant, messaging, multi-user collaboration, real-time analytics, file-sharing and notifications.

Let's head to making a real-time chat application with node.js and Socket.io

🍁Real-time Chat Application

🍀Implementation in Node.js

🍂Basic Code Explanation Overview:

  • const socketio = require("socket.io") //to get the package

  • const io = socketio(server) //here server is http-server

  • io.on('connect', ()=>{}) // this is the first function called when the connection is made between the client and server.

  • socket.on('join',()=>{}) and socket.join('user room') // these events for joining the room

  • socket.emit('name of event',()=>{}) //it emits the event which both client or server will listen to

  • socket.on('name of event',()+>{}) //This is used both on the client side and server side to listen to the event which is emitted by socket.emit and run the code inside it

  • socket.on('disconnect',()=>{}) //it will tell when the user has disconnected

🍂Server Side Integration

cd MyApp 
yarn init // setup npm package
yarn add --save express // install express.js
yarn add --save socket.io // for socket.io

After everything is installed. Create a file name server.js and started writing the code.

🍂server.js

const express = require('express');
const socketio = require('socket.io');
const app = express();
const io = socketio(server);

const {
    addUser,
    removeUser,
    getUser,
    getUsersInRoom
} = require('./utils/User');

io.on('connect', socket=>{
    socket.on('join',({username,room}, callback)=>{
        const {error, user} = addUser({ id: socket.id, name:username, room}); //add user with socket id and room info
        if(error) return callback(error);
        socket.join(user.room);
        socket.emit('message',{
            user: 'adminX',
            text: `${user.name.toUpperCase()}, Welcome to ${user.room} room.`
});
        socket.broadcast.to(user.room).emit('message',{
            user: 'adminX',
            text: `${user.name.toUpperCase()} has joined!`
});
        io.to(user.room).emit('roomData',{
            room: user.room,
            users: getUserInRoom(user.room) // get user data based on user's room
});
    callback();
})
    socket.on('sendMessage',(message,callback)=>{
        const user = getUser(socker.id);
        io.to(user.room).emit('message',{user: user.name, text: message});
        callback();
})
    socket.on('disconnect',()=>{
        const user = removeUser(socket.id);
        if(user){
            io.to(user.room).emit('message',{
                user: 'adminX';
                text: `${user.name.toUpperCase()} has left.` 
});
        io.to(user.room).emit('roomData',{
            room: user.room,
            users: getUsersInRoom(user.room)
});
}
});
server.listen(process.env.PORT || 3000, ()=>{
    console.log('Server is running');
});
});

After establishing the connection and user joins the room. There will be a socket.emit event which will be listened by socket.on

socket.on('sendMessage') event will be listened which contains user name and message. After this connection will be disconnected when user leaves the chat and an event will be emitted.

Create a user.js file for user crud.

🍂users.js

const users =[];
const addUser = ({id, name, room})=>{
    name = name.trim().toLowerCase();
    room = room.trim().toLowerCase();
    const existingUser = users.find(
     user => user.room === room && user.name === name
);
    if(!name || !room) return { error: 'Username and room are required.'};
    if(existingUSer) return { error: 'Username already exists.'};
    const user = {id, name , room};
    users.push(user);
    return {user};
};

const removeUser = id =>{
    const index = users.findIndex(user =>user.id === id);
    if (index !== -1) return users.splice(index, 1)[0];
};
const getUser = id => users.find(user => user.id === id);

const getUsersInRoom = room => users.filter(user => user.room === room);

module.exports = { addUser, removeUser, getUser, getUsersInRoom};

Let's use react for the front-end part.

🍂Client code integration

create and cd frontend
yarn add --save socket.io-client // for socket.io client

🍂ClientSocket.js

import { useState, useEffect, useRef} from 'react';
import socketIOClient from 'socket.io-client';

const ENDPOINT = 'http://localhost:3000/';

const ClientSocket = ({ username, room })=>{
    const socketRef = useRef();
    const [users, setUsers] = useState([]);
    const [messages, setMessages] = useState([]);
    const [error, setError] = useState('');

    useEffect(()=>{
        socketRef.current = socketIOClient(ENDPOINT);
        socketRef.current.emit('join',{username, room}, error=>{
            if(error){
               setError(error);
}
});
    return ()=>{
        socketRef.current.emit('disconnect');
};
},[username, room]);

useEffect(()=>{
    socketRef.current.on('message',message=>{
        setMessages(messages=> [..messages, message]);
});
    socketRef.current.on('roomData',({users})=>{
        setUsers(users);
})
},[]);
const sendMessage = message =>{
    socketRef.current.emit('sendMessage',message, ()=>{});
};
return { users, message, sendMessage, error };
};
export default ClientSocket;

🍂Chat.js

const Chat=()=>{
    const history = useHistory();
    const {useranme, setMessage} = useParams();
    const [message, setMessage] = useState('');
    const {users, messages, sendMessage} = ClientSocket({
        username,
        room
});
    const onSubmitHandler = event =>{
        event.preventDefault();
        if(message) sendMessage(message);
        setMessage('');
};
    const renderActiveUsers = ()=>{
        return users.map(user=>{
            return (
              <p key= {user.id}>
                <i className="fa fa-circle"></i>{user.name}
              </p>
);
});
};
return (
    <div className="container-fluid d-flex">
      <div className="col-lg-6">
        <ul>
          <h6>Messages</h6>
            {messages.text}
        </ul>
        <form onSubmit={onSubmitHandler}>
          <input
            autoComplete="off"
            value={message}
            onChange={e=> setMessage(e.target.value)}
            type="text"
            name="message"
          />
            <button type="submit">Send</button>
      </form>
    </div>
<div className="col-lg-6 border-left ml-2">
    {renderActiveUsers()}
</div>
</div>
)
};
export default Chat;

That's all for the frontend and backend part of socket.IO. It's an awesome and easy to integrate library.

Thanks for reading.😊

🍁References

  1. https://socket.io/docs/v4/

  2. https://medium.com/swlh/build-a-chat-room-with-node-js-and-socket-io-e3e43f49a02d

Did you find this article valuable?

Support Sagar Medtiya by becoming a sponsor. Any amount is appreciated!