Eventos enviados por el servidor - HTML

-

Cómo funcionan los Server Sent Events

Los Server Sent Events (SSE) son una tecnología que permite a un servidor enviar datos a un navegador cliente en tiempo real. La arquitectura de SSE se basa en un modelo de comunicación unidireccional, donde el servidor envía datos al cliente, pero el cliente no envía datos de vuelta al servidor.

En SSE, el cliente se conecta al servidor usando la API EventSource. El servidor mantiene esta conexión abierta y envía eventos al cliente cada vez que hay nuevos datos disponibles. El cliente recibe estos eventos y los procesa a medida que llegan, actualizando la página web o desencadenando otras acciones según sea necesario.

La comunicación entre el cliente y el servidor en SSE se realiza a través de una conexión HTTP estándar. El servidor envía eventos como un flujo de datos de texto, con cada evento separado por una línea en blanco. El cliente utiliza la API EventSource para escuchar estos eventos y manejarlos en consecuencia.

Consejo: Ejemplo de una conexión EventSource

const eventSource = new EventSource('http://example.com/sse');

eventSource.onmessage = function(event) {
    console.log(event.data);
};

Una diferencia clave entre SSE y WebSockets es el modelo de comunicación. Mientras que SSE es unidireccional, con el servidor enviando datos al cliente, WebSockets permite una comunicación bidireccional, donde tanto el cliente como el servidor pueden enviarse datos mutuamente.

Consejo: Ejemplo de una conexión WebSocket

const socket = new WebSocket('ws://example.com/ws');

socket.onopen = function(event) {
    socket.send('Hello Server!');
};

socket.onmessage = function(event) {
    console.log(event.data);
};

Otra diferencia es el formato de los mensajes. En SSE, los mensajes se envían como texto plano, con cada mensaje compuesto por una o más líneas de datos de texto. En cambio, WebSockets utiliza un formato de mensaje binario, que puede ser más eficiente para enviar grandes cantidades de datos.

A pesar de estas diferencias, tanto SSE como WebSockets tienen su lugar en el desarrollo web moderno. SSE es adecuado para escenarios donde el servidor necesita enviar actualizaciones al cliente en tiempo real, como feeds de noticias o cotizaciones de bolsa. WebSockets, por otro lado, son ideales para aplicaciones que requieren comunicación bidireccional, como aplicaciones de chat o juegos multijugador.

Configuración de Eventos Enviados por el Servidor

Configurar Eventos Enviados por el Servidor implica ajustar tanto el servidor como el cliente. En el lado del servidor, necesitas preparar un script que envíe eventos al cliente. En el lado del cliente, debes crear un objeto EventSource para recibir y manejar estos eventos.

Configuración del Servidor

Para configurar el servidor para enviar eventos, necesitas preparar un script que responda a las solicitudes del cliente con los encabezados y datos de eventos correctos. El servidor debe enviar una respuesta con un encabezado Content-Type de "text/event-stream", que indica al cliente que debe esperar un flujo de eventos.

Consejo: Código del lado del servidor usando Node.js y el framework Express

const express = require('express');
const app = express();

app.get('/sse', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    setInterval(() => {
        const eventData = `data: La hora del servidor es ${new Date()}\n\n`;
        res.write(eventData);
    }, 1000);
});

app.listen(3000, () => {
    console.log('Servidor iniciado en el puerto 3000');
});

El servidor envía un nuevo evento al cliente cada segundo, conteniendo la hora actual del servidor.

Consejo: Código del lado del servidor usando PHP

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$time = date('r');
echo "data: La hora del servidor es: {$time}\n\n";
flush();
?>

El script PHP envía un solo evento al cliente, conteniendo la hora actual del servidor.

Configuración del Cliente

En el lado del cliente, necesitas crear un objeto EventSource y especificar la URL del script del lado del servidor que envía los eventos. El objeto EventSource se conectará automáticamente al servidor y comenzará a recibir eventos.

Consejo: Código del lado del cliente usando JavaScript

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    console.log(event.data);
};

eventSource.onerror = (error) => {
    console.error('Error de EventSource:', error);
};

El cliente crea un objeto EventSource y especifica la URL del script del lado del servidor ('/sse'). El manejador de eventos onmessage se llama cada vez que se recibe un nuevo evento, y el manejador de eventos onerror se llama si ocurre un error.

También puedes manejar tipos de eventos específicos añadiendo escuchadores de eventos:

Consejo: Manejo de tipos de eventos específicos

eventSource.addEventListener('timestamp', (event) => {
    console.log('Evento de marca de tiempo:', event.data);
});

El cliente escucha eventos de tipo 'timestamp' y registra los datos del evento en la consola.

Con el código del lado del servidor y del cliente en su lugar, tu aplicación está ahora configurada para usar Eventos Enviados por el Servidor para comunicación en tiempo real.

Envío y Recepción de Eventos

Envío de Eventos desde el Servidor

Para enviar eventos desde el servidor al cliente usando Server Sent Events (SSE), debes crear y enviar mensajes de eventos formateados. Los eventos tienen una o más líneas de texto, con cada línea separada por dos puntos y un espacio. Cada evento termina con un carácter de doble salto de línea (\n\n).

Los campos de evento más comunes son:

Campo Descripción
event Especifica el tipo de evento. Si se omite, el tipo de evento por defecto es "message".
data Contiene los datos del evento. Puedes enviar múltiples líneas de datos incluyendo múltiples campos data.
id Asigna un ID único al evento. Esto ayuda con la reconexión y el manejo de errores.
retry Establece el tiempo de reconexión (en milisegundos) para el cliente si se pierde la conexión.

Consejo: Envío de Eventos del Lado del Servidor

app.get('/sse', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    const sendEvent = (eventType, data) => {
        const eventData = `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
        res.write(eventData);
    };

    // Envía un evento "timestamp" cada segundo
    setInterval(() => {
        const timestamp = new Date().toLocaleTimeString();
        sendEvent('timestamp', { time: timestamp });
    }, 1000);

    // Envía un evento "message" cuando se cumple una condición
    if (someCondition) {
        sendEvent('message', { text: '¡Hola desde el servidor!' });
    }
});

Recepción de Eventos en el Cliente

En el lado del cliente, puedes escuchar eventos usando el objeto EventSource. El manejador de eventos onmessage se llama cuando se recibe un evento "message", y puedes definir manejadores de eventos personalizados para tipos de eventos específicos usando addEventListener.

Consejo: Escucha de Eventos del Lado del Cliente

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Mensaje recibido:', data.text);
};

eventSource.addEventListener('timestamp', (event) => {
    const data = JSON.parse(event.data);
    console.log('Marca de tiempo recibida:', data.time);
});

Puedes manejar diferentes tipos de eventos añadiendo más escuchadores de eventos:

Consejo: Manejo de Múltiples Tipos de Eventos

eventSource.addEventListener('userConnected', (event) => {
    const data = JSON.parse(event.data);
    console.log(`Usuario ${data.userId} conectado`);
});

eventSource.addEventListener('userDisconnected', (event) => {
    const data = JSON.parse(event.data);
    console.log(`Usuario ${data.userId} desconectado`);
});

Al enviar y recibir eventos, tu aplicación puede establecer un canal de comunicación en tiempo real entre el servidor y el cliente, permitiéndote actualizar al cliente con nuevos datos tan pronto como estén disponibles en el servidor.

Manejo de Errores y Reconexión

Al usar Server Sent Events (SSE), debes manejar errores y reconectarte al servidor si se pierde la conexión. Esto ayuda a crear una aplicación más confiable y resistente.

El manejo de errores del lado del cliente implica escuchar el evento onerror en el objeto EventSource. Este evento se dispara cuando ocurre un error, como un problema de red o un inconveniente con el script del lado del servidor.

Consejo: Manejar errores del lado del cliente

const eventSource = new EventSource('/sse');

eventSource.onerror = (error) => {
    console.error('Error de EventSource:', error);
    // Realizar manejo de errores, como actualizar la interfaz de usuario o registrar el error
};

Reconectarse al servidor después de una pérdida de conexión es otro aspecto importante del manejo de errores. Por defecto, el objeto EventSource intentará reconectarse al servidor si la conexión se cierra. Sin embargo, puedes controlar el comportamiento de reconexión usando las propiedades withCredentials y reconnectionTime.

Consejo: Controlar el comportamiento de reconexión

const eventSource = new EventSource('/sse', {
    withCredentials: true,
    reconnectionTime: 5000
});

La propiedad withCredentials, cuando se establece en true, envía cookies y encabezados de autenticación con la solicitud de EventSource, lo que puede ser útil para mantener una sesión a través de reconexiones.

La propiedad reconnectionTime establece el tiempo (en milisegundos) de espera antes de intentar reconectarse al servidor. Por defecto, el objeto EventSource usará una estrategia de retroceso exponencial, donde el tiempo de reconexión aumenta con cada intento fallido.

Mejores Prácticas para el Manejo de Errores y Reconexión
1. Implementar manejo de errores del lado del cliente: Escuchar el evento onerror y manejar los errores de manera elegante, como actualizando la interfaz de usuario o registrando el error.
2. Establecer un tiempo de reconexión apropiado: Elegir un tiempo de reconexión que equilibre la necesidad de actualizaciones en tiempo real con el potencial de sobrecargar el servidor con demasiados intentos de reconexión.
3. Usar latidos del lado del servidor: Implementar latidos del lado del servidor (mensajes periódicos enviados por el servidor) para detectar y manejar conexiones perdidas más rápidamente.
4. Manejar errores del lado del servidor: Asegurarse de que tu script del lado del servidor pueda manejar errores y enviar mensajes de error apropiados al cliente.
5. Usar mecanismos de respaldo: Si SSE no es compatible o la conexión falla, considera usar métodos alternativos, como sondeo largo o WebSockets.

Compatibilidad con navegadores y alternativas

Los eventos enviados por el servidor (SSE) son compatibles con la mayoría de los navegadores web modernos, incluyendo Chrome, Firefox, Safari y Edge. Sin embargo, algunos navegadores más antiguos pueden no admitir SSE de forma nativa, lo que significa que necesitas proporcionar opciones alternativas para mantener la compatibilidad.

En 2023, la compatibilidad global de los navegadores con SSE es de alrededor del 95%, según Can I Use. Esto significa que la mayoría de los usuarios podrán utilizar SSE sin problemas. Sin embargo, sigue siendo importante considerar opciones alternativas para el 5% restante de usuarios que pueden estar utilizando navegadores más antiguos o menos comunes.

Una opción alternativa para los navegadores que no admiten SSE es utilizar sondeo largo (long-polling). El sondeo largo es una técnica donde el cliente envía una solicitud al servidor y espera una respuesta. Si el servidor no tiene nuevos datos, mantiene la solicitud abierta hasta que haya nuevos datos disponibles, luego envía la respuesta de vuelta al cliente. El cliente entonces envía inmediatamente otra solicitud, repitiendo el proceso. Aunque no es tan eficiente como SSE, el sondeo largo puede proporcionar un nivel similar de funcionalidad en tiempo real.

Otra opción alternativa es utilizar WebSockets. Los WebSockets proporcionan un canal de comunicación bidireccional entre el cliente y el servidor, permitiendo la transferencia de datos en tiempo real en ambas direcciones. Si un navegador no admite SSE, puedes verificar si admite WebSockets y usarlo como alternativa.

Si prefieres un enfoque más uniforme, puedes utilizar polyfills o bibliotecas que proporcionan soporte SSE para navegadores más antiguos. Un polyfill es un fragmento de código que replica la funcionalidad de una API de navegador más nueva en un navegador más antiguo. Por ejemplo, el polyfill EventSource de Yaffle se puede utilizar para añadir soporte SSE a navegadores más antiguos que no tienen soporte nativo.

También hay bibliotecas como Pusher y Socket.IO que proporcionan funcionalidad en tiempo real utilizando una variedad de técnicas, incluyendo SSE, WebSockets y sondeo largo. Estas bibliotecas pueden abstraer las diferencias entre navegadores y proporcionar una API consistente para la comunicación en tiempo real.

Consejo: Uso de un Polyfill para añadir soporte SSE a navegadores más antiguos

<!-- Incluir el polyfill de EventSource -->
<script src="https://cdn.jsdelivr.net/npm/event-source-polyfill@1.0.9/src/eventsource.min.js"></script>

<script>
if (!window.EventSource) {
    // Si el navegador no admite EventSource, usar el polyfill
    window.EventSource = window.EventSourcePolyfill;
}

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    console.log(event.data);
};
</script>

Al incluir el polyfill de EventSource y comprobar si el navegador admite EventSource de forma nativa, puedes asegurarte de que la funcionalidad SSE esté disponible para todos los usuarios, independientemente de su navegador.

En resumen, aunque SSE es compatible con la mayoría de los navegadores modernos, es importante considerar opciones alternativas y polyfills para navegadores más antiguos. Al usar técnicas como sondeo largo, WebSockets o bibliotecas que abstraen las diferencias entre navegadores, puedes proporcionar una experiencia en tiempo real consistente para todos tus usuarios.

Ventajas y Desventajas de SSE

Los Eventos Enviados por el Servidor (SSE) tienen varias ventajas y desventajas en comparación con otras tecnologías de comunicación en tiempo real como WebSockets y long-polling.

Ventajas

Una de las principales ventajas de SSE es que permite obtener actualizaciones en tiempo real del servidor sin necesidad de consultarlo repetidamente. Con SSE, el servidor puede enviar actualizaciones al cliente cuando hay nuevos datos disponibles, lo que significa que el cliente siempre tiene la información más actualizada sin tener que enviar solicitudes al servidor.

SSE también es ligero en comparación con WebSockets. WebSockets utiliza una conexión persistente y bidireccional entre el cliente y el servidor, lo cual puede ser excesivo para situaciones donde solo se necesita que el servidor envíe actualizaciones al cliente. SSE, por otro lado, usa una conexión HTTP regular para enviar actualizaciones, lo que significa que tiene menos sobrecarga y puede ser más eficiente en términos de recursos del servidor.

Otra ventaja de SSE es que es más simple de implementar que WebSockets. Con SSE, solo se necesita configurar un script del lado del servidor que envíe eventos al cliente, y un script del lado del cliente que escuche esos eventos usando la API EventSource. WebSockets, por otro lado, requiere una configuración más compleja que involucra un proceso de negociación y una actualización de protocolo.

Desventajas

Una desventaja de SSE es que solo admite comunicación unidireccional del servidor al cliente. Esto significa que el cliente no puede enviar mensajes de vuelta al servidor usando SSE. Si se necesita comunicación bidireccional, se deberá usar WebSockets u otra tecnología que lo soporte.

Otra desventaja de SSE es que tiene un soporte de navegador limitado en comparación con otras tecnologías como long-polling. Si bien la mayoría de los navegadores modernos admiten SSE, algunos navegadores más antiguos no tienen soporte nativo para ello. Esto significa que puede ser necesario proporcionar opciones alternativas o usar polyfills para asegurarse de que la aplicación funcione en todos los navegadores.

Consejo: HTML code with extra spaces

<p>This    is   a   paragraph   with    extra   spaces.</p>

Cuando un navegador renderiza este código, mostrará el texto como:

This is a paragraph with extra spaces.

Consejo: Mismatched tags

<p>This is a paragraph.</div>

En este caso, la etiqueta de apertura <p> se cierra con una etiqueta </div>, lo cual es incorrecto. La forma correcta de cerrar el párrafo es:

<p>This is a paragraph.</p>

Casos de uso y ejemplos de SSE

Los Eventos Enviados por el Servidor (SSE) son adecuados para muchos escenarios de aplicaciones web en tiempo real. Veamos algunos casos de uso comunes y ejemplos donde se puede utilizar SSE para proporcionar una mejor experiencia de usuario.

Un caso de uso común para SSE es para actualizaciones de datos en tiempo real. Una aplicación de ticker de acciones podría usar SSE para enviar actualizaciones de precios de acciones en tiempo real al cliente. Cada vez que el precio de una acción cambia en el servidor, puede enviar un evento al cliente con el precio actualizado. El cliente puede entonces actualizar la interfaz de usuario para mostrar el nuevo precio sin que el usuario tenga que refrescar la página.

Consejo: Actualizaciones de acciones en tiempo real

const express = require('express');
const app = express();

// Datos de acciones simulados
let stockData = {
    'AAPL': 150.23,
    'GOOGL': 2321.01,
    'MSFT': 300.45
};

// Enviar actualizaciones de acciones cada 5 segundos
setInterval(() => {
    // Actualizar precios de acciones aleatoriamente
    for (const symbol in stockData) {
        stockData[symbol] += Math.random() * 10 - 5;
    }

    // Enviar datos de acciones actualizados a los clientes
    sendEvent('stockUpdate', stockData);
}, 5000);

// ...
const eventSource = new EventSource('/sse');

eventSource.addEventListener('stockUpdate', (event) => {
    const stockData = JSON.parse(event.data);

    for (const symbol in stockData) {
        const price = stockData[symbol].toFixed(2);
        document.getElementById(symbol).textContent = price;
    }
});

Otro caso de uso para SSE es enviar notificaciones y alertas al cliente. Una aplicación de redes sociales podría usar SSE para notificar a los usuarios cuando reciben un nuevo mensaje o cuando alguien los menciona en una publicación. El servidor puede enviar un evento al cliente cada vez que hay una nueva notificación disponible, y el cliente puede mostrar la notificación al usuario.

SSE también se puede usar para aplicaciones de chat en vivo. Cuando un usuario envía un mensaje, el servidor puede transmitir ese mensaje a todos los clientes conectados usando SSE. Cada cliente escucharía los eventos de nuevos mensajes y actualizaría la interfaz de usuario del chat en consecuencia. Sin embargo, ten en cuenta que SSE es unidireccional, por lo que necesitarías usar otro método (como una solicitud HTTP POST regular) para enviar mensajes del cliente al servidor.

SSE se puede usar para análisis y métricas en tiempo real. Una aplicación de monitoreo de servidores podría usar SSE para enviar métricas del servidor en tiempo real al cliente. El cliente podría mostrar estas métricas en un panel de control, dando a los administradores una vista en tiempo real del rendimiento del servidor.

Consejo: Métricas de servidor en tiempo real

// Enviar métricas del servidor cada segundo
setInterval(() => {
    const serverMetrics = {
        cpu: os.loadavg()[0],
        memory: os.freemem() / os.totalmem()
    };

    sendEvent('serverMetrics', serverMetrics);
}, 1000);
eventSource.addEventListener('serverMetrics', (event) => {
    const metrics = JSON.parse(event.data);

    document.getElementById('cpuUsage').textContent = `${metrics.cpu.toFixed(2)}%`;
    document.getElementById('memoryUsage').textContent = `${(metrics.memory * 100).toFixed(2)}%`;
});

Estos son algunos ejemplos de cómo se puede usar SSE en aplicaciones web en tiempo real. La naturaleza unidireccional de SSE lo hace adecuado para escenarios donde el servidor necesita enviar actualizaciones al cliente, pero el cliente no necesita enviar datos de vuelta al servidor.