Skip to main content

Overview

Protocol: WebSocket with automatic HTTP long-polling fallback Communication: Bidirectional (Client ↔ Server) Endpoint: wss://api.petstoreapi.com When to Use Socket.IO:
  • ✅ Real-time features that must work everywhere
  • ✅ Customer support chat systems
  • ✅ Need automatic fallback when WebSocket unavailable
  • ✅ Room-based messaging
  • ✅ Broadcast capabilities
When NOT to Use Socket.IO:
  • ❌ You need raw WebSocket (use WebSocket protocol instead)
  • ❌ Simple server → client updates (use SSE instead)

How Socket.IO Works

Client                                    Server
  │                                         │
  ├───────── WebSocket Connection ─────────>│
  │          (or falls back to polling)      │
  │                                         │
  ├───────── joinChat (room) ─────────────>│
  │<──────────── connected ─────────────────┤
  │                                         │
  ├───────── chatMessage ─────────────────>│
  │<───────── chatMessage (broadcast) ─────┤
  │                                         │
Key Features:
  • Automatic reconnection
  • Room-based messaging
  • Broadcast to multiple clients
  • Automatic fallback to HTTP long-polling
  • Binary data support

Connection

JavaScript (Browser)

import { io } from 'socket.io-client';

// Connect to Socket.IO server
const socket = io('wss://api.petstoreapi.com', {
  auth: {
    token: 'YOUR_JWT_TOKEN'
  },
  transports: ['websocket', 'polling'] // Try WebSocket, fall back to polling
});

// Connection events
socket.on('connect', () => {
  console.log('Connected to Socket.IO');
  console.log('Socket ID:', socket.id);
});

socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
  // Socket.IO will auto-reconnect
});

socket.on('connect_error', (error) => {
  console.error('Connection error:', error);
});

JavaScript (Node.js)

const { io } = require('socket.io-client');

const socket = io('wss://api.petstoreapi.com', {
  auth: {
    token: process.env.PETSTORE_API_TOKEN
  },
  transports: ['websocket'],
  reconnection: true,
  reconnectionDelay: 1000,
  reconnectionAttempts: 5
});

socket.on('connect', () => {
  console.log('Connected!');
});

socket.on('message', (data) => {
  console.log('Received:', data);
});

Python (python-socketio)

import socketio

# Create Socket.IO client
sio = socketio.Client()

@sio.event
def connect():
    print('Connected to Socket.IO')

@sio.event
def disconnect():
    print('Disconnected')

@sio.on('chatMessage')
def on_chat_message(data):
    print(f'Message: {data["content"]}')

# Connect with authentication
sio.connect(
    'https://api.petstoreapi.com',
    auth={'token': 'YOUR_JWT_TOKEN'},
    transports=['websocket']
)

Customer Support Chat

Join Chat Room

socket.emit('joinChat', {
  conversationId: 'conv_abc123',
  userId: 'user_xyz789'
});

// Server confirms
socket.on('joinedChat', (data) => {
  console.log('Joined chat:', data.conversationId);
  console.log('Participants:', data.participants);
});

Send Chat Message

socket.emit('chatMessage', {
  conversationId: 'conv_abc123',
  content: 'I need help with my order',
  timestamp: new Date().toISOString()
});

Receive Messages

socket.on('chatMessage', (data) => {
  console.log('New message:', data.content);
  console.log('From:', data.senderId);
  console.log('Time:', data.timestamp);

  // Update UI
  displayMessage(data);
});

Agent Assignment

socket.on('agentAssigned', (data) => {
  console.log('Agent assigned:', data.agentId);
  console.log('Agent name:', data.agentName);

  // Show agent info in UI
  showAgentInfo(data);
});

Typing Indicators

Send Typing Started

function startTyping() {
  socket.emit('typingStarted', {
    conversationId: 'conv_abc123'
  });
}

// Call when user starts typing
inputField.addEventListener('input', startTyping);

Send Typing Stopped

let typingTimeout;

function stopTyping() {
  clearTimeout(typingTimeout);
  typingTimeout = setTimeout(() => {
    socket.emit('typingStopped', {
      conversationId: 'conv_abc123'
    });
  }, 1000);
}

// Call when user stops typing
inputField.addEventListener('input', stopTyping);

Receive Typing Indicators

socket.on('typingStarted', (data) => {
  if (data.userId !== currentUserId) {
    showTypingIndicator(`${data.userId} is typing...`);
  }
});

socket.on('typingStopped', (data) => {
  if (data.userId !== currentUserId) {
    hideTypingIndicator();
  }
});

Rooms

Join a Room

socket.emit('joinRoom', {
  room: 'pet-updates',
  userId: 'user_xyz789'
});

socket.on('joinedRoom', (data) => {
  console.log(`Joined room: ${data.room}`);
});

Leave a Room

socket.emit('leaveRoom', {
  room: 'pet-updates'
});

Send to Room

socket.emit('messageToRoom', {
  room: 'pet-updates',
  message: {
    type: 'pet-availability',
    petId: 'pet_abc123',
    status: 'AVAILABLE'
  }
});

Advanced Usage

Reconnection Management

const socket = io('wss://api.petstoreapi.com', {
  reconnection: true,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
  reconnectionAttempts: 5,
  randomizationFactor: 0.5
});

// Monitor reconnection attempts
socket.on('reconnect', (attemptNumber) => {
  console.log(`Reconnected after ${attemptNumber} attempts`);
});

socket.on('reconnect_attempt', (attemptNumber) => {
  console.log(`Reconnection attempt ${attemptNumber}`);
});

socket.on('reconnect_failed', () => {
  console.log('Failed to reconnect');
});

Message Acknowledgment

// Send message with acknowledgment
socket.emit('chatMessage', {
  conversationId: 'conv_abc123',
  content: 'Hello'
}, (acknowledgment) => {
  if (acknowledgment.error) {
    console.error('Failed to send:', acknowledgment.error);
  } else {
    console.log('Message delivered:', acknowledgment.messageId);
  }
});

Binary Data

// Send image
const fileInput = document.querySelector('#image-upload');

fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();

  reader.onload = () => {
    socket.emit('uploadImage', {
      conversationId: 'conv_abc123',
      file: reader.result, // Base64 encoded
      filename: file.name,
      mimeType: file.type
    });
  };

  reader.readAsDataURL(file);
});

Error Handling

Connection Errors

socket.on('connect_error', (error) => {
  console.error('Connection error:', error);

  if (error.message.includes('Authentication failed')) {
    console.error('Invalid token');
  } else if (error.message.includes('Transport error')) {
    console.error('Network error, will retry...');
  }
});

Message Errors

socket.on('error', (error) => {
  console.error('Socket.IO error:', error);
  showNotification(`Error: ${error.message}`);
});

Best Practices

1. Cleanup on Disconnect

window.addEventListener('beforeunload', () => {
  socket.emit('leaveChat', {
    conversationId: 'conv_abc123'
  });
  socket.disconnect();
});

2. Message Queue

class MessageQueue {
  constructor(socket) {
    this.socket = socket;
    this.queue = [];
    this.isConnected = false;

    this.socket.on('connect', () => {
      this.isConnected = true;
      this.flush();
    });
  }

  send(event, data) {
    if (this.isConnected) {
      this.socket.emit(event, data);
    } else {
      this.queue.push({ event, data });
    }
  }

  flush() {
    while (this.queue.length > 0 && this.isConnected) {
      const { event, data } = this.queue.shift();
      this.socket.emit(event, data);
    }
  }
}

const queue = new MessageQueue(socket);
queue.send('chatMessage', { content: 'Hello' });

3. Rate Limiting

class RateLimiter {
  constructor(socket, maxMessages = 10, perSeconds = 1) {
    this.socket = socket;
    this.maxMessages = maxMessages;
    this.perSeconds = perSeconds;
    this.messages = [];
  }

  emit(event, data) {
    const now = Date.now();
    const cutoff = now - (this.perSeconds * 1000);

    // Remove old messages
    this.messages = this.messages.filter(t => t > cutoff);

    if (this.messages.length < this.maxMessages) {
      this.socket.emit(event, data);
      this.messages.push(now);
      return true;
    } else {
      console.warn('Rate limited');
      return false;
    }
  }
}

const limiter = new RateLimiter(socket, 10, 1);

Troubleshooting

Connection Fails

  • Check authentication token
  • Verify server URL
  • Check browser console for CORS errors
  • Try enabling polling transport

Not Receiving Messages

  • Verify you’ve joined the correct room
  • Check event name matches exactly
  • Ensure socket is connected
  • Check server logs for errors

High Memory Usage

  • Clean up event listeners
  • Don’t store entire message history
  • Disconnect when not needed

Comparison with WebSocket

FeatureSocket.IOWebSocket
Fallback✅ HTTP polling❌ No fallback
Reconnection✅ Automatic⚠️ Manual
Rooms✅ Built-in⚠️ Manual
Broadcasting✅ Built-in⚠️ Manual
Binary Support✅ Yes✅ Yes
Library Required✅ Yes❌ Native
OverheadMediumLow
Choose Socket.IO when:
  • You need fallback mechanisms
  • Building chat/collaboration features
  • Want built-in room/broadcast support
Choose WebSocket when:
  • Maximum performance needed
  • Want native browser support
  • Building custom protocol

Interactive Documentation