What is Socket.IO?
The features provided by Socket.IO
-
Real-Time Communication:Socket.IO enables real-time, low-latency communication between clients and servers, allowing for instant data exchange.
-
Bidirectional Data Flow:Both clients and servers can send and receive data simultaneously, enabling interactive and responsive applications.
-
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.
-
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.
-
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.
-
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.
-
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?
-
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 clientsocket.emit('new message', 'Welcome to the chat!');});
-
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 clientsocket.on('new message', (msg) => {console.log('New message received:', msg);});
-
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 codeio.on('connection', (socket) => {socket.on(new message', (msg) => {// Broadcast to all clients except the sendersocket.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.
-
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 codeio.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 codeio.on('connection', (socket) => {socket.on(new message', (roomName, msg) => {// Broadcast to all clients in the specified roomio.to(roomName).emit(new message', msg);
// Broadcast to all clients in room1 and room2io.to('room1').to('room2').emit('chat message', msg);});});
Building a real-time chat application with Socket.IO
-
Initialize a new project:mkdir socketio-chatcd socketio-chatnpm init -y
-
Install Express and Socket.IO:npm install express socket.io
-
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 roomio.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 channelsocket.join(room);socket.room = room;});
socket.on('chat-message', (data) => {data.sender = socket.id// Broadcast to all clients in the specified roomio.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.
<!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
References
- https://socket.io/docs/v4/
- https://apidog.com/articles/socket-io-vs-websocket/
- https://ably.com/topic/scaling-socketio (image source)