Carlos Seijas
← Volver a los blogs

¿Qué son los WebSockets? Guía completa y ejemplos

Código
WebSocketsTiempo RealJavaScriptNode.jsSocket.ioComunicaciónWeb
¿Qué son los WebSockets? Guía completa y ejemplos

¿Has usado Google Docs y te has quedado hipnotizado viendo cómo aparece el cursor de otra persona escribiendo en tiempo real? ¿O has jugado algún juego online donde todo sucede instantáneamente? Yo recuerdo la primera vez que vi esto funcionando y pensé "¡esto es pura magia!". Resulta que no es magia, son WebSockets, una tecnología que cambió completamente la forma en que las aplicaciones web pueden comunicarse.

WebSockets: la revolución de la comunicación web

Los WebSockets son como tener una línea telefónica directa entre tu navegador y el servidor. Imagínate la diferencia entre mandar cartas (HTTP tradicional) y tener una conversación telefónica (WebSockets). Con las cartas, tienes que esperar respuesta, pero con el teléfono pueden hablar los dos al mismo tiempo, cuando quieran.

La gran diferencia con HTTP (que todo cambia)

// HTTP tradicional - Solo el cliente puede iniciar
// 1. Cliente solicita → 2. Servidor responde → 3. Conexión se cierra

// WebSockets - Comunicación bidireccional
// 1. Cliente conecta → 2. Conexión permanente → 3. Ambos pueden enviar datos

Esta capacidad de comunicación bidireccional en tiempo real es lo que hace posible esas cosas increíbles que usamos todos los días:

¿Cómo funciona esta magia por dentro?

El "apretón de manos" inicial (handshake)

Los WebSockets son educados: primero se presentan con un saludo HTTP normal, y luego se "actualizan" a WebSocket para quedarse charlando:

// Lado del cliente
const socket = new WebSocket('ws://localhost:8080');

socket.onopen = function(event) {
    console.log('Conexión WebSocket establecida');
    socket.send('Hola servidor!');
};

socket.onmessage = function(event) {
    console.log('Mensaje recibido:', event.data);
};

socket.onclose = function(event) {
    console.log('Conexión cerrada');
};

Los cuatro estados de una conexión (como estados de ánimo)

Los WebSockets tienen personalidad y te dicen exactamente cómo se sienten:

  1. CONNECTING (0): "Estoy tratando de conectar..."
  2. OPEN (1): "¡Listo! Podemos hablar"
  3. CLOSING (2): "Me estoy despidiendo..."
  4. CLOSED (3): "Ya no estoy aquí"
// Verificar el estado de la conexión
console.log(socket.readyState);
// 0: CONNECTING, 1: OPEN, 2: CLOSING, 3: CLOSED

Manos a la obra: código que funciona de verdad

Un cliente WebSocket que no te va a fallar

class ChatClient {
    constructor(url) {
        this.socket = new WebSocket(url);
        this.setupEventHandlers();
    }
    
    setupEventHandlers() {
        this.socket.onopen = (event) => {
            console.log('Conectado al servidor');
            this.onConnectionOpen();
        };
        
        this.socket.onmessage = (event) => {
            const message = JSON.parse(event.data);
            this.handleMessage(message);
        };
        
        this.socket.onerror = (error) => {
            console.error('Error WebSocket:', error);
        };
        
        this.socket.onclose = (event) => {
            console.log('Desconectado del servidor');
            this.handleDisconnection();
        };
    }
    
    sendMessage(message) {
        if (this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify(message));
        }
    }
    
    handleMessage(message) {
        // Procesar mensajes recibidos
        console.log('Mensaje recibido:', message);
    }
}

// Uso
const client = new ChatClient('ws://localhost:8080');

Un servidor que puede manejar a todos tus usuarios

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
    console.log('Nuevo cliente conectado');
    
    // Enviar mensaje de bienvenida
    ws.send(JSON.stringify({
        type: 'welcome',
        message: 'Bienvenido al servidor WebSocket'
    }));
    
    // Escuchar mensajes del cliente
    ws.on('message', function incoming(data) {
        const message = JSON.parse(data);
        console.log('Mensaje recibido:', message);
        
        // Reenviar mensaje a todos los clientes conectados
        wss.clients.forEach(function each(client) {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(JSON.stringify(message));
            }
        });
    });
    
    // Manejar desconexión
    ws.on('close', function close() {
        console.log('Cliente desconectado');
    });
});

Casos reales donde WebSockets brillan

1. Chat en vivo (el clásico que nunca falla)

// Cliente de chat
class LiveChat {
    constructor(username) {
        this.username = username;
        this.socket = new WebSocket('ws://localhost:8080');
        this.setupChat();
    }
    
    setupChat() {
        this.socket.onmessage = (event) => {
            const data = JSON.parse(event.data);
            this.displayMessage(data);
        };
    }
    
    sendMessage(text) {
        const message = {
            type: 'chat',
            username: this.username,
            message: text,
            timestamp: new Date().toISOString()
        };
        this.socket.send(JSON.stringify(message));
    }
    
    displayMessage(data) {
        const messageElement = document.createElement('div');
        messageElement.innerHTML = `
            <strong>${data.username}:</strong> ${data.message}
            <small>${new Date(data.timestamp).toLocaleTimeString()}</small>
        `;
        document.getElementById('messages').appendChild(messageElement);
    }
}

2. Notificaciones que aparecen como por arte de magia

// Sistema de notificaciones
class NotificationSystem {
    constructor() {
        this.socket = new WebSocket('ws://localhost:8080');
        this.setupNotifications();
    }
    
    setupNotifications() {
        this.socket.onmessage = (event) => {
            const notification = JSON.parse(event.data);
            this.showNotification(notification);
        };
    }
    
    showNotification(notification) {
        // Mostrar notificación en la interfaz
        const notificationEl = document.createElement('div');
        notificationEl.className = 'notification';
        notificationEl.textContent = notification.message;
        document.body.appendChild(notificationEl);
        
        // Remover después de 3 segundos
        setTimeout(() => {
            notificationEl.remove();
        }, 3000);
    }
}

3. Colaboración en tiempo real (como Google Docs, pero tuyo)

// Editor colaborativo simple
class CollaborativeEditor {
    constructor(documentId) {
        this.documentId = documentId;
        this.socket = new WebSocket('ws://localhost:8080');
        this.setupEditor();
    }
    
    setupEditor() {
        this.socket.onmessage = (event) => {
            const change = JSON.parse(event.data);
            if (change.type === 'text_change') {
                this.applyRemoteChange(change);
            }
        };
        
        // Detectar cambios locales
        document.getElementById('editor').addEventListener('input', (e) => {
            this.broadcastChange(e.target.value);
        });
    }
    
    broadcastChange(content) {
        const change = {
            type: 'text_change',
            documentId: this.documentId,
            content: content,
            timestamp: Date.now()
        };
        this.socket.send(JSON.stringify(change));
    }
    
    applyRemoteChange(change) {
        const editor = document.getElementById('editor');
        editor.value = change.content;
    }
}

WebSockets vs el resto del mundo

La comparación que todos necesitamos

| Tecnología | Latencia | Overhead | Complejidad | Caso de uso | |------------|----------|----------|-------------|-------------| | WebSockets | Muy baja | Bajo | Media | Tiempo real | | Long Polling | Media | Alto | Alta | Pseudo tiempo real | | Server-Sent Events | Baja | Medio | Baja | Solo servidor → cliente | | HTTP Requests | Alta | Muy alto | Baja | Datos estáticos |

Cuándo usar WebSockets (y cuándo NO)

// ✅ Bueno para:
// - Chats en vivo
// - Juegos multijugador
// - Actualizaciones de estado frecuentes
// - Colaboración en tiempo real

// ❌ No necesario para:
// - Formularios simples
// - Páginas estáticas
// - APIs REST tradicionales
// - Carga de datos una sola vez

Ojo que no todo necesita ser en tiempo real. A veces es como usar un Ferrari para ir al súper de la esquina: funciona, pero es overkill.

Lo que he aprendido usando WebSockets en producción

1. Siempre ten un plan B (reconexión automática)

class RobustWebSocket {
    constructor(url) {
        this.url = url;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.reconnectDelay = 1000;
        this.connect();
    }
    
    connect() {
        this.socket = new WebSocket(this.url);
        
        this.socket.onopen = () => {
            console.log('Conectado');
            this.reconnectAttempts = 0;
        };
        
        this.socket.onclose = () => {
            if (this.reconnectAttempts < this.maxReconnectAttempts) {
                setTimeout(() => {
                    this.reconnectAttempts++;
                    this.connect();
                }, this.reconnectDelay);
            }
        };
    }
}

2. Valida todo lo que llegue (la paranoia es buena)

function validateMessage(message) {
    try {
        const data = JSON.parse(message);
        return data.type && data.payload;
    } catch (error) {
        return false;
    }
}

socket.onmessage = (event) => {
    if (validateMessage(event.data)) {
        handleMessage(JSON.parse(event.data));
    }
};

3. No satures la conexión (rate limiting)

class RateLimitedSocket {
    constructor(url, messagesPerSecond = 10) {
        this.socket = new WebSocket(url);
        this.messageQueue = [];
        this.messagesPerSecond = messagesPerSecond;
        this.startMessageProcessor();
    }
    
    send(message) {
        this.messageQueue.push(message);
    }
    
    startMessageProcessor() {
        setInterval(() => {
            if (this.messageQueue.length > 0) {
                const message = this.messageQueue.shift();
                this.socket.send(JSON.stringify(message));
            }
        }, 1000 / this.messagesPerSecond);
    }
}

Mi experiencia personal con WebSockets

Cuando implementé mi primer chat con WebSockets, me quedé fascinado viendo los mensajes aparecer instantáneamente. Era como descubrir que tu aplicación web tenía superpoderes. Pero también aprendí algunas lecciones importantes a la mala.

Lo importante es entender que WebSockets no son solo una tecnología; son una forma completamente diferente de pensar sobre la comunicación web. En lugar de "pregunta y respuesta", es "conversación continua".

En mi experiencia, la parte más difícil no es implementar WebSockets, sino manejar todos los casos edge: qué pasa cuando se cae la conexión, qué hacer con mensajes perdidos, cómo manejar usuarios que se reconectan. Pero una vez que tienes eso resuelto, es increíble lo que puedes construir.

Una cosa que me sorprendió es cuánto mejora la experiencia del usuario. Los usuarios se acostumbran súper rápido a las actualizaciones en tiempo real, y después cualquier app que requiera refresh les parece antigua.

WebSockets han democratizado la creación de aplicaciones en tiempo real. Antes necesitabas infraestructura súper compleja para hacer un chat o actualizaciones en vivo. Ahora cualquiera puede hacerlo con unas pocas líneas de código.

¿Has usado WebSockets en algún proyecto? ¿Cuál fue tu primera experiencia viendo datos actualizarse en tiempo real? ¿O tienes alguna idea loca de algo que quisieras hacer con comunicación en tiempo real? Me encanta escuchar cómo otros developers descubren y usan esta tecnología.

Comentarios

Posts relacionados