Capítulo 14 EventSource

Los EventSource (también conocidos como Server-Sent Events), son eventos en tiempo real transmitidos por el servidor y recibidos en el navegador. Son similares a los WebSockets en que suceden el tiempo real, pero son principalmente un método de comunicación unidireccional desde el servidor. Al igual que en los WebSocket, creamos una nueva conexión indicando la URL, y el navegador intentará conectarse inmediatamente. El objeto EventSource dispone de los siguientes eventos:

  • open: se dispara cuando la conexión se ha establecido.
  • message: evento que indica la llegada de un mensaje nuevo.
  • error: se dispara cuando algo ha ido mal.

Lo que hace a EventSource diferente es la manera en que controla las pérdidas de conexión y la gestión de los mensajes.

Si la conexión se pierde por alguna razón, el API automáticamente trata de volver a conectarse. Además, al restablecer la conexión, el cliente envía al servidor la ID del último mensaje que recibió. Esto permite al servidor, enviar al cliente todos los mensajes que no ha podido recibir. No es necesario realizar ninguna configuración especial en nuestro código, simplemente el servidor nos enviará los mensajes que no hemos recibido.

Un sencillo ejemplo:

var es = new EventSource('/bidding');
 
es.onopen = function () {
    initialiseData();
};
 
es.onmessage = function (event) {
    var data = JSON.parse(event.data);
    updateData(data.time, data.bid);
};

14.1 EventSource en el servidor

En el lado del servidor podemos seguir utilizando una solución basada en PHP y la pila completa LAMP, pero como Apache no se comporta de manera estable con conexiones persistentes, constantemente trata de cerrar las conexiones y EventSource trata de volver a conectarse automáticamente. Esto da como resultado un comportamiento más parecido a Ajax que a una comunicación unidireccional y en tiempo real desde el servidor.

Realmente, esta no es la mejor manera de aprovechar las ventajas de EventSource. Para ello, necesitamos una conexión persistente con el servidor, y LAMP no nos lo puede proporcionar. Actualmente existen soluciones de servidor basadas en eventos, como pueden ser Node.js (un servidor basado en JavaScript) o Twisted para Python.

14.1.1 Un simple servidor para EventSource

El siguiente código muestra como crear un servidor muy simple con Node.js, el cual acepta conexiones y envía mensajes a los clientes conectados. En este caso, únicamente se notifica al resto de usuarios conectados al servicio, que un nuevo usuario se ha conectado.

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/event-stream',
                        'Cache-Control': 'no-cache'});
    // get the last event id and convert to a number
    var lastId = req.headers['last-event-id']*1;
    if (lastId) {
        for (var i = lastId; i < eventId; i++) {
            res.write('data: ' + JSON.stringify(history[eventId])
                               + '\nid: ' + eventId + '\n');
        }
    }
 
    // finally cache the response connection
    connections.push(res);
 
   // When a regular web request is received
    connections.forEach(function (response) {
        history[++eventId] = { agent: req.headers['user-agent'],
                               time: +new Date };
        res.write('data: ' + JSON.stringify(history[eventId])
                           + '\nid: ' + eventId + '\n');
    });
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

En el lado del cliente, el código sería tan sencillo como el siguiente:

var es = new EventSource('/eventsource');
es.onmessage = function (event) {
    var data = JSON.parse(event.data);
    log.innerHTML += '<li><strong>' + data.agent
                     + '</strong><br> connected at <em>'
                     + (new Date(data.time)) + '</em></li>';
};

Una aplicación muy simple, pero que nos da una idea del funcionamiento de los eventos en tiempo real, utilizando un servidor basado en eventos.

Índice de contenidos