Capítulo 13 WebSockets

13.1 Introducción

Internet se ha creado en gran parte a partir del llamado paradigma solicitud/respuesta de HTTP. Un cliente carga una página web, se cierra la conexión y no ocurre nada hasta que el usuario hace clic en un enlace o envía un formulario.

Hace ya algún tiempo que existen tecnologías que permiten al servidor enviar datos al cliente en el mismo momento que detecta que hay nuevos datos disponibles. Se conocen como "Push" o "Comet". Uno de los trucos más comunes para crear la ilusión de una conexión iniciada por el servidor se denomina Long Polling. Con el Long Polling, el cliente abre una conexión HTTP con el servidor, el cual la mantiene abierta hasta que se envíe una respuesta. Cada vez que el servidor tenga datos nuevos, enviará la respuesta. El Long Polling y otras técnicas funcionan bastante bien y de hecho ha sido utilizadas en muchas aplicaciones como el chat de Gmail.

Los WebSockets nos ofrecen una conexión bidireccional entre el servidor y el navegador. Esta conexión se produce en tiempo real y se mantiene permanentemente abierta hasta que se cierre de manera explícita. Esto significa que cuando el servidor quiere enviar datos al servidor, el mensaje se traslada inmediatamente. Efectivamente, esto es lo que sucedía al utilizar tecnologías como Comet, pero se conseguía utilizando una serie de trucos. Si esto no funcionada, siempre era posible utilizar Ajax para conseguir un resultado parecido, pero sobrecargando el servidor de manera innecesaria.

Si disponemos de un socket abierto, el servidor puede enviar datos a todos los clientes conectados a ese socket, sin tener que estar constantemente procesando peticiones de Ajax. La ventaja en cuanto a rendimiento y escalabilidad es bastante evidente al utilizar WebSockets.

La latencia en las comunicaciones es otro de los beneficios de utilizar WebSockets. Como el socket está siempre abierto y escuchando, los datos son enviados inmediatamente desde el servidor al navegador, reduciendo el tiempo al mínimo, en comparación con un paradigma basado en Ajax, donde hay que realizar una petición, procesar la respuesta y enviarla de nuevo de vuelta.

Finalmente, los datos a transmitir se reducen también de manera drástica, pasando de un mínimo de 200-300 bytes en peticiones Ajax, a 10-20 bytes utilizando websockets.

13.2 Crear un WebSocket

El API de WebSocket es realmente sencillo de utilizar. Actualmente, los navegadores únicamente soportan el envío de cadenas de caracteres, y se realiza de una manera muy similar a la que utilizábamos para enviar mensajes en los Web Workers. El API está limitado a métodos para abrir la conexión, enviar y recibir datos y cerrar la conexión.

Para abrir una conexión WebSocket, sólo tenemos que ejecutar el constructor WebSocket, que toma como parámetro la URL del socket a abrir. Hay que tener en cuenta que el protocolo a utilizar es ws://:

var socket = new WebSocket('ws://html5rocks.websocket.org/tweets');

También existe un protocolo wss:// para conexiones WebSocket seguras, de la misma forma que se utiliza https:// para las conexiones HTTP seguras.

La URL que utilizamos para conectarnos con el WebSocket no tiene por qué pertenecer al mismo dominio que nuestro documento, por lo que podemos conectarnos a servicios de terceros sin problemas, expandiendo las posibilidades de nuestra aplicación.

13.3 Comunicación con el servidor

Cuando se establece una conexión con el servidor (cuando el evento open se activa), se puede empezar a enviar datos al servidor con el método send a través del socket creado.

// Send new Tweet
socket.send("Hey there, I'm using WebSockets");

De la misma forma, el servidor puede enviarnos mensajes en cualquier momento. Cada vez que esto ocurra, se activa el evento onmessage. Los datos enviados por el servidor se encuentran en la propiedad data del objeto event.

socket.onmessage = function(event) {
    var data = JSON.parse(event.data);
    if (data.action == 'joined') {
        initiliseChat();
    } else {
        showNewMessage(data.who, data.text);
    }
});

El API incorpora además dos eventos que se disparan cuando el socket se abre y está listo, y cuando éste se va a cerrar:

socket.onopen  = function(e){ log("Welcome - status "+this.readyState); };
socket.onclose = function(e){ log("Disconnected - status "+this.readyState); };

13.4 WebSocket en el servidor

Al utilizar los WebSocket, se crea un patrón de uso completamente nuevo para las aplicaciones de servidor. Aunque las pilas de servidor tradicionales como LAMP están diseñadas a partir del ciclo de solicitud-respuesta de HTTP, a menudo dan problemas si hay muchas conexiones WebSocket abiertas. Mantener un gran número de conexiones abiertas de forma simultánea requiere una arquitectura capaz de recibir un alto nivel de concurrencia sin consumir muchos recursos. Estas arquitecturas suelen estar basadas en subprocesos o sistemas de E/S asíncronos.

En el próximo capítulo sobre Server-Sent Events, veremos una implementación de un servidor web basado en JavaScript, llamado Node.js.

Ejercicio 15

Ver enunciado