Skip to content

elyse502/node-socket.io

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔌 Node.js Socket.IO Mastery 🚀

Real-Time Communication Learning Project

Node.js Socket.IO Express JavaScript HTML5 License

Mastering Real-Time Web Technologies:

WebSockets Event-Driven Bidirectional Low Latency


📋 Table of Contents


🎯 Project Overview

This project is a comprehensive learning exploration of Socket.IO, the leading library for real-time, bidirectional, event-based communication between web clients and servers. Built from scratch, it demonstrates how to create interactive applications with persistent connections, real-time data exchange, and instant event handling.

Why Socket.IO?

Feature Traditional HTTP Socket.IO
Connection Request-response, stateless Persistent, stateful
Communication One-way (client → server) Bidirectional (client ↔ server)
Real-time Polling required Native real-time
Overhead High per request Low per message
Latency High (new connection each time) Low (persistent connection)
Use Cases REST APIs, page loads Chat, gaming, live updates

✨ Features

Category Feature Status
Connection Management Real-time persistent connections
Event System Custom event emission and handling
Message Broadcasting Send messages to all connected clients
Room Management Group clients into logical rooms
Namespace Support Isolated communication channels
Disconnect Handling Automatic cleanup on client disconnect
Error Recovery Automatic reconnection with backoff
Multiple Clients Handle concurrent connections
Server Events Emit events from server to clients
Client Events Emit events from clients to server

🏗️ Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                           CLIENT LAYER (Browser)                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                      Socket.IO Client Library                        │   │
│  │  • Establishes WebSocket connection                                  │   │
│  │  • Manages reconnection logic                                        │   │
│  │  • Emits and listens to events                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         UI Components                                │   │
│  │  • Event displays                                                   │   │
│  │  • Message input forms                                              │   │
│  │  • Connection status indicators                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────┬───────────────────────────────────────────┘

                    WebSocket / HTTP Long-Polling

┌─────────────────────────────────┴───────────────────────────────────────────┐
│                           SERVER LAYER (Node.js)                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         HTTP Server                                  │   │
│  │  • Serves static files (HTML, CSS, JS)                              │   │
│  │  • Handles initial page loads                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                      Socket.IO Server                                │   │
│  │  • Manages all client connections                                   │   │
│  │  • Handles event routing                                            │   │
│  │  • Manages rooms and namespaces                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        Event Handlers                               │   │
│  │  • connection: New client joined                                    │   │
│  │  • message: User sent message                                       │   │
│  │  • disconnect: Client left                                          │   │
│  │  • custom-event: Application-specific logic                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

🛠️ Tech Stack

Technology Version Purpose Badge
Node.js 18+ JavaScript Runtime Node.js
Express.js 4.x Web Framework Express
Socket.IO 4.x Real-time Engine Socket.IO
HTML5 - Structure HTML5
JavaScript ES6+ Client Logic JavaScript

📁 Project Structure

node-socket.io/

├── 📁 client/                         # Frontend application
│   ├── 📄 index.html                  # Main UI page
│   ├── 📄 script.js                   # Active client logic
│   ├── 📄 script_0.js                 # Learning iteration 0
│   ├── 📄 script_1.js                 # Learning iteration 1
│   ├── 📄 package.json                # Client dependencies
│   └── 📄 package-lock.json           # Locked dependencies

├── 📁 server/                         # Backend application
│   ├── 📄 server.js                   # Active server logic
│   ├── 📄 server_0.js                 # Learning iteration 0
│   ├── 📄 server_1.js                 # Learning iteration 1
│   ├── 📄 server_2.js                 # Learning iteration 2
│   ├── 📄 package.json                # Server dependencies
│   └── 📄 package-lock.json           # Locked dependencies

├── 📄 .gitignore                      # Git ignore rules
└── 📄 README.md                       # Documentation

Learning Iterations

File Topic Covered
server_0.js / script_0.js Basic connection establishment
server_1.js / script_1.js Event emission and handling
server_2.js / script_2.js Broadcasting and rooms

🚀 Getting Started

Prerequisites

  • Node.js (v18 or higher)
  • npm or yarn package manager
  • Modern web browser (Chrome, Firefox, Edge)

Installation

# 1. Clone the repository
git clone https://github.com/elyse502/node-socket.io.git

# 2. Navigate to project directory
cd node-socket.io

# 3. Install server dependencies
cd server
npm install

# 4. Install client dependencies (if any)
cd ../client
npm install

# 5. Return to root directory
cd ..

Running the Application

# Start the server (from server directory)
cd server
npm run devStart
# OR for development with auto-reload
nodemon server.js

# Open browser and navigate to
http://localhost:3000

Quick Start Commands

# Development mode (with auto-restart)
npm run dev

# Production mode
npm start

# Alternative port (modify server.js)
PORT=8080 npm start

🔌 Socket.IO Concepts Covered

1. Basic Connection

// Server
const io = require("socket.io")(server);
io.on("connection", (socket) => {
  console.log("New client connected:", socket.id);
});

// Client
const socket = io("http://localhost:3000");
socket.on("connect", () => {
  console.log("Connected to server");
});

2. Event Emission

// Client to Server
socket.emit("message", { text: "Hello Server!" });

// Server to Client
socket.emit("welcome", { message: "Welcome to the chat!" });

// Server to All Clients (Broadcast)
io.emit("announcement", { text: "New user joined!" });

3. Event Listening

// Server
socket.on("message", (data) => {
  console.log("Received:", data);
  // Process and possibly respond
});

// Client
socket.on("welcome", (data) => {
  console.log("Server says:", data.message);
});

4. Disconnection Handling

// Server
socket.on("disconnect", (reason) => {
  console.log("Client disconnected:", socket.id, "Reason:", reason);
});

// Client
socket.on("disconnect", (reason) => {
  console.log("Disconnected from server:", reason);
});

5. Broadcasting

// Send to all clients except sender
socket.broadcast.emit("user-joined", { userId: socket.id });

// Send to all clients including sender
io.emit("global-update", { status: "active" });

// Send to specific room
io.to("room1").emit("room-message", { text: "Hello Room 1!" });

📡 Event Flow

┌──────────┐                                    ┌──────────┐
│  Client  │                                    │  Server  │
│    A     │                                    │          │
└────┬─────┘                                    └────┬─────┘
     │                                               │
     │  1. socket.emit('message', data)             │
     │──────────────────────────────────────────────>│
     │                                               │
     │                                               │ 2. Process message
     │                                               │    Save to DB
     │                                               │    Broadcast to others
     │                                               │
     │  3. socket.broadcast.emit('message', data)   │
     │<──────────────────────────────────────────────│
     │                                               │
     │  4. socket.on('message', handler)            │
     │  Display message                             │
     │                                               │
     │  5. User disconnects                         │
     │──────────────────────────────────────────────>│
     │                                               │
     │  6. io.emit('user-left', userId)             │
     │<──────────────────────────────────────────────│
     │                                               │
     │  7. Update UI - remove user                  │
     │                                               │

🎨 Client Features

Connection Status Indicators

// Monitor connection state
socket.on("connect", () => {
  updateStatus("Connected", "green");
});

socket.on("disconnect", () => {
  updateStatus("Disconnected", "red");
});

socket.on("connect_error", (error) => {
  updateStatus("Connection Error", "orange");
});

Automatic Reconnection

// Socket.IO handles reconnection automatically
// Configure reconnection options
const socket = io({
  reconnection: true,
  reconnectionAttempts: 5,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
});

Message Handling

<!-- Sample HTML Structure -->
<div id="messages">
  <div class="message received">
    <strong>User:</strong> Hello!
    <small>10:30 AM</small>
  </div>
</div>
<input type="text" id="messageInput" placeholder="Type a message..." />
<button onclick="sendMessage()">Send</button>

🖥️ Server Features

Connection Management

const io = require("socket.io")(server, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
});

// Track connected users
const users = new Map();

io.on("connection", (socket) => {
  users.set(socket.id, { id: socket.id, joinedAt: Date.now() });
  console.log(`Total users: ${users.size}`);

  socket.on("disconnect", () => {
    users.delete(socket.id);
    console.log(`Total users: ${users.size}`);
  });
});

Room Management

// Join a room
socket.on("join-room", (roomName) => {
  socket.join(roomName);
  socket.to(roomName).emit("user-joined", socket.id);
});

// Leave a room
socket.on("leave-room", (roomName) => {
  socket.leave(roomName);
  socket.to(roomName).emit("user-left", socket.id);
});

// Send to room
socket.to("chat-room").emit("message", data);

📊 Example Usage

Basic Chat Application

Server (server.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("client"));

io.on("connection", (socket) => {
  console.log("User connected:", socket.id);

  // Broadcast to others that user joined
  socket.broadcast.emit("user-joined", socket.id);

  // Handle incoming messages
  socket.on("chat-message", (message) => {
    // Broadcast message to all clients
    io.emit("chat-message", {
      userId: socket.id,
      text: message,
      timestamp: new Date().toISOString(),
    });
  });

  socket.on("disconnect", () => {
    console.log("User disconnected:", socket.id);
    io.emit("user-left", socket.id);
  });
});

server.listen(3000, () => {
  console.log("Server running on port 3000");
});

Client (script.js):

const socket = io();

// DOM elements
const messageForm = document.getElementById("message-form");
const messageInput = document.getElementById("message-input");
const messagesDiv = document.getElementById("messages");

// Send message
messageForm.addEventListener("submit", (e) => {
  e.preventDefault();
  const message = messageInput.value;
  if (message) {
    socket.emit("chat-message", message);
    messageInput.value = "";
  }
});

// Receive message
socket.on("chat-message", (data) => {
  const messageElement = document.createElement("div");
  messageElement.className = "message";
  messageElement.innerHTML = `
        <strong>${data.userId}</strong>
        <span>${data.text}</span>
        <small>${new Date(data.timestamp).toLocaleTimeString()}</small>
    `;
  messagesDiv.appendChild(messageElement);
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
});

// User joined/left notifications
socket.on("user-joined", (userId) => {
  addSystemMessage(`User ${userId} joined the chat`);
});

socket.on("user-left", (userId) => {
  addSystemMessage(`User ${userId} left the chat`);
});

🔧 Configuration

Server Configuration Options

const io = socketIo(server, {
  // CORS settings
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
    credentials: true,
  },

  // Transport options
  transports: ["websocket", "polling"],

  // Connection options
  pingTimeout: 60000,
  pingInterval: 25000,

  // Upgrade options
  allowUpgrades: true,
  upgradeTimeout: 10000,

  // Cookie options
  cookie: {
    name: "io",
    httpOnly: true,
    sameSite: "lax",
  },
});

Client Configuration Options

const socket = io({
  // Connection URL
  path: "/socket.io",

  // Transport options
  transports: ["websocket", "polling"],

  // Reconnection options
  reconnection: true,
  reconnectionAttempts: Infinity,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,

  // Timeout options
  timeout: 20000,

  // Query parameters
  query: {
    token: "your-auth-token",
    userId: "123",
  },

  // Authentication
  auth: {
    token: "your-jwt-token",
  },
});

🧪 Testing

Testing Multiple Clients

# Terminal 1 - Start server
cd server
npm run devStart

# Terminal 2 - Open first client
open http://localhost:3000

# Terminal 3 - Open second client (incognito/private window)
open http://localhost:3000

Testing Events with cURL (WebSocket not supported)

# Use wscat for WebSocket testing
npm install -g wscat
wscat -c ws://localhost:3000/socket.io/?EIO=4&transport=websocket

Debugging

// Enable debug logging
localStorage.debug = 'socket.io-client:*';

// Or in Node.js
DEBUG=socket.io:* node server.js

🐛 Troubleshooting

Issue Solution
Connection refused Check if server is running on correct port
CORS errors Configure CORS options in server
Connection drops Increase pingTimeout and pingInterval
Messages not received Check event names match exactly
Multiple connections Ensure socket.io client imported only once
Reconnection not working Check reconnection options configuration
Large messages failing Implement message chunking or increase limits

📈 Performance Considerations

Aspect Recommendation Why
Message Size Keep < 10KB Large messages fragment and increase latency
Event Frequency Limit to < 100/sec Too many events overwhelm the connection
Connected Clients Scale horizontally Single server handles ~10,000 connections
Rooms Clean up empty rooms Prevents memory leaks
Binary Data Use binary transfers More efficient for images/files
Compression Enable per-message deflate Reduces bandwidth usage

Scaling Tips

// Use Redis adapter for multiple server instances
const redisAdapter = require("@socket.io/redis-adapter");
const redis = require("redis");

const pubClient = redis.createClient();
const subClient = pubClient.duplicate();

io.adapter(redisAdapter(pubClient, subClient));

🔮 Advanced Topics to Explore

Topic Description Difficulty
Namespaces Separate communication channels ⭐⭐
Rooms Group-based messaging ⭐⭐
Acknowledgements Confirmation of message receipt ⭐⭐⭐
Middleware Authentication and logging ⭐⭐⭐
Redis Adapter Horizontal scaling ⭐⭐⭐⭐
Binary Streaming File and data streaming ⭐⭐⭐⭐
Custom Parsers Optimize data serialization ⭐⭐⭐⭐⭐

📚 Learning Resources

Official Documentation

Learning Path

  1. Week 1: Basic connections and events
  2. Week 2: Broadcasting and rooms
  3. Week 3: Error handling and reconnection
  4. Week 4: Building a complete chat application
  5. Week 5: Advanced features (acknowledgements, middleware)
  6. Week 6: Scaling with Redis and production deployment

👨‍💻 Author

Elysée NIYIBIZI

Junior Fullstack Software Engineer

Portfolio GitHub LinkedIn Email


📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments

  • Socket.IO Team - For the amazing real-time library
  • Node.js Community - For continuous support
  • Open Source Contributors - For making real-time web accessible

⭐ Star this repository if it helped you learn Socket.IO!

Built with 💻, WebSockets, and Real-Time Passion


From basic connections to production-ready real-time applications - this is your complete Socket.IO learning journey!

⬆ Back to Top