Presentación de WebSockets: el ingreso de los sockets a la Web

El problema: Las conexiones cliente-servidor y servidor-cliente de baja latencia

La Web se construyó, en gran medida, en torno al paradigma de solicitud/respuesta de HTTP. Un cliente carga una página web y no sucede nada hasta que hace clic en la página siguiente. Alrededor del 2005, AJAX comenzó a hacer que la web se sintiera más dinámica. Aun así, el cliente dirigía todas las comunicaciones HTTP, lo que requería la interacción del usuario o sondeos periódicos para cargar datos nuevos del servidor.

Hace bastante tiempo que existen tecnologías que permiten que el servidor envíe datos al cliente en el momento preciso en que sabe que hay nuevos datos disponibles. Suelen tener nombres como "Push" o "Comet". Uno de los hackeos más comunes para crear la ilusión de una conexión iniciada por el servidor se denomina sondeo largo. Con el sondeo largo, el cliente abre una conexión HTTP al servidor que la mantiene abierta hasta que se envía la respuesta. Cada vez que el servidor tiene datos nuevos, envía la respuesta (otras técnicas involucran solicitudes Flash, varias partes XHR y lo que se denomina htmlfiles). Las encuestas largas y las otras técnicas funcionan bastante bien. Los usas a diario en aplicaciones como el chat de Gmail.

Sin embargo, todas estas soluciones alternativas comparten un problema: tienen la sobrecarga de HTTP, lo que no las hace adecuadas para aplicaciones de baja latencia. Piensa en juegos de disparos en primera persona multijugador en el navegador o en cualquier otro juego en línea con un componente en tiempo real.

Presentamos WebSocket: el acceso de los sockets a la Web

La especificación WebSocket define una API que establece conexiones de "socket" entre un navegador web y un servidor. En palabras sencillas: Existe una conexión persistente entre el cliente y el servidor, y ambas partes pueden comenzar a enviar datos en cualquier momento.

Getting Started

Abre una conexión de WebSocket con solo llamar al constructor de WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Observa el ws:. Este es el nuevo esquema de URL para las conexiones de WebSocket. También existe wss: para la conexión segura de WebSocket de la misma forma en que se usa https: para las conexiones HTTP seguras.

Adjuntar algunos controladores de eventos de inmediato a la conexión te permite saber cuándo se abre la conexión, cuándo se recibieron mensajes entrantes o se produjo un error.

El segundo argumento acepta subprotocolos opcionales. Puede ser una cadena o un array de cadenas. Cada cadena debe representar el nombre de un subprotocolo, y el servidor acepta solo uno de los subprotocolos pasados en el array. El subprotocolo aceptado se puede determinar accediendo a la propiedad protocol del objeto WebSocket.

Los nombres de los subprotocolos deben ser uno de los nombres de subprotocolo registrados en el registro de IANA. Actualmente solo hay un nombre de subprotocolo (jabon) registrado desde febrero de 2012.

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Cómo comunicarse con el servidor

En cuanto tengamos una conexión con el servidor (cuando se activa el evento open), podremos comenzar a enviar datos al servidor mediante el método send('your message') en el objeto de conexión. Solía admitir solo cadenas, pero en la última especificación ahora también puede enviar mensajes binarios. Para enviar datos binarios, puedes usar los objetos Blob o ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Del mismo modo, el servidor puede enviarnos mensajes en cualquier momento. Cuando esto sucede, se activa la devolución de llamada onmessage. La devolución de llamada recibe un objeto de evento, y se puede acceder al mensaje en sí a través de la propiedad data.

WebSocket también puede recibir mensajes binarios en la especificación más reciente. Los marcos binarios se pueden recibir en formato Blob o ArrayBuffer. Para especificar el formato del objeto binario recibido, establece la propiedad binaryType del objeto WebSocket en “blob” o “arraybuffer”. El formato predeterminado es “blob”. (No es necesario alinear el parámetro binaryType en el envío).

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Otra función agregada recientemente de WebSocket son las extensiones. Con las extensiones, podrás enviar marcos comprimidos, multiplexados, etc. Para encontrar las extensiones aceptadas por el servidor, examina la propiedad de extensiones del objeto WebSocket después del evento abierto. Todavía no hay especificación de extensiones publicadas oficialmente desde febrero de 2012.

// Determining accepted extensions
console.log(connection.extensions);

Comunicación de origen cruzado

Como es un protocolo moderno, la comunicación de origen cruzado está integrada directamente en WebSocket. Si bien debes asegurarte de comunicarte únicamente con clientes y servidores de confianza, WebSocket permite la comunicación entre las partes de cualquier dominio. El servidor decide si el servicio estará disponible para todos los clientes o solo para aquellos que residen en un conjunto de dominios bien definidos.

Servidores proxy

Cada tecnología nueva conlleva un nuevo conjunto de problemas. En el caso de WebSocket, es la compatibilidad con los servidores proxy los que median las conexiones HTTP en la mayoría de las redes de la empresa. El protocolo WebSocket usa el sistema de actualización HTTP (que normalmente se usa para HTTP/SSL) a fin de "actualizar" una conexión HTTP a una conexión de WebSocket. A algunos servidores proxy no les gusta esto y se interrumpirá la conexión. Por lo tanto, incluso si un cliente determinado usa el protocolo WebSocket, tal vez no sea posible establecer una conexión. Esto hace que la siguiente sección sea aún más importante :)

Usa WebSockets hoy mismo

WebSocket aún es una tecnología nueva y no está completamente implementada en todos los navegadores. Sin embargo, puedes usar WebSocket con las bibliotecas que usan uno de los resguardos mencionados anteriormente siempre que WebSocket no esté disponible. Una biblioteca que se volvió muy popular en este dominio es socket.io, que viene con un cliente y una implementación de servidor del protocolo e incluye resguardos (socket.io aún no admite mensajes binarios a partir de febrero de 2012). También hay soluciones comerciales, como PusherApp, que se pueden integrar fácilmente en cualquier entorno web si se proporciona una API de HTTP para enviar mensajes de WebSocket a los clientes. Debido a la solicitud HTTP adicional, siempre habrá una sobrecarga adicional en comparación con WebSocket puro.

El lado del servidor

El uso de WebSocket crea un patrón de uso completamente nuevo para las aplicaciones del servidor. Si bien las pilas de servidores tradicionales, como LAMP, están diseñadas para el ciclo de solicitud/respuesta HTTP, a menudo no funcionan bien con una gran cantidad de conexiones abiertas de WebSocket. Mantener una gran cantidad de conexiones abiertas al mismo tiempo requiere una arquitectura que reciba simultaneidad alta a un costo de bajo rendimiento. Estas arquitecturas suelen diseñarse en torno a subprocesos o IO sin bloqueo.

Implementaciones del servidor

Versiones de protocolo

El protocolo de conexión (un protocolo de enlace y la transferencia de datos entre el cliente y el servidor) para WebSocket ahora es RFC6455. Las versiones más recientes de Chrome y Chrome para Android son totalmente compatibles con RFC6455, incluida la mensajería binaria. Además, Firefox será compatible con la versión 11, Internet Explorer en la versión 10. Puedes seguir usando versiones de protocolo anteriores, pero no se recomienda hacerlo porque se sabe que son vulnerables. Si tienes implementaciones de servidor para versiones anteriores del protocolo WebSocket, te recomendamos que las actualices a la versión más reciente.

casos de uso

Usa WebSocket siempre que necesites una conexión de latencia baja y casi en tiempo real entre el cliente y el servidor. Ten en cuenta que esto podría implicar reconsiderar la forma en que compilas tus aplicaciones del servidor con un nuevo enfoque en tecnologías como las colas de eventos. Estos son algunos ejemplos de casos de uso:

  • Juegos multijugador en línea
  • Aplicaciones de chat
  • Visor de deportes en vivo
  • Novedades de redes sociales que se actualizan en tiempo real

Demostraciones

Referencias