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

El problema: Conexiones cliente-servidor y servidor-cliente de baja latencia

La Web se creó en gran medida en torno al llamado paradigma de solicitud/respuesta de HTTP. Un cliente carga una página web y no sucede nada hasta que el usuario hace clic en la página siguiente. Alrededor de 2005, AJAX comenzó a hacer que la Web se sintiera más dinámica. Sin embargo, el cliente controlaba toda la comunicación HTTP, lo que requería la interacción del usuario o la sondeo periódico para cargar datos nuevos desde el servidor.

Las tecnologías que permiten que el servidor envíe datos al cliente en el momento en que sabe que hay datos nuevos disponibles existen desde hace bastante tiempo. Tienen nombres 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 sondeo largo. Con la votación prolongada, 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 incluyen Flash, solicitudes XHR multipart y los llamados archivos html). Los sondeos largos y las demás técnicas funcionan bastante bien. Los usas todos los días en aplicaciones como el chat de Gmail.

Sin embargo, todas estas soluciones 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 cualquier otro juego en línea con un componente en tiempo real.

Presentamos WebSocket: Llevando sockets a la Web

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

Comenzar

Para abrir una conexión de WebSocket, simplemente llama 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 manera que se usa https: para las conexiones HTTP seguras.

Si adjuntas algunos controladores de eventos de inmediato a la conexión, puedes saber cuándo se abre la conexión, se reciben mensajes entrantes o se produce un error.

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

Los nombres de los subprotocolos deben ser uno de los nombres de subprotocolos registrados en el registro de IANA. Actualmente, solo hay un nombre de subprotocolo (soap) registrado a partir de 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);
};

Comunicación con el servidor

En cuanto tengamos una conexión con el servidor (cuando se active el evento open), podremos comenzar a enviar datos al servidor con el método send('your message') en el objeto de conexión. Antes solo admitía cadenas, pero en la especificación más reciente también puede enviar mensajes binarios. Para enviar datos binarios, puedes usar el objeto 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. Cada vez que esto sucede, se activa la devolución de llamada de onmessage. La devolución de llamada recibe un objeto de evento y se puede acceder al mensaje real a través de la propiedad data.

WebSocket también puede recibir mensajes binarios en la especificación más reciente. Las tramas binarias 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 que alinees el parámetro binaryType durante 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 nueva de WebSocket son las extensiones. Con las extensiones, será posible enviar tramas comprimidas, multiplexadas, etc. Para encontrar las extensiones aceptadas por el servidor, examina la propiedad extensions del objeto WebSocket después del evento de apertura. A febrero de 2012, aún no hay una especificación de extensiones publicada oficialmente.

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

Comunicación de origen cruzado

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

Servidores proxy

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

Usar WebSockets hoy

WebSocket es una tecnología reciente y no se implementa por completo en todos los navegadores. Sin embargo, puedes usar WebSocket hoy con bibliotecas que usan uno de los resguardos mencionados anteriormente cuando WebSocket no está disponible. Una biblioteca que se ha vuelto muy popular en este dominio es socket.io, que incluye una implementación del protocolo para clientes y servidores, y también 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 proporcionando una API de HTTP para enviar mensajes de WebSocket a los clientes. Debido a la solicitud HTTP adicional, siempre habrá una sobrecarga extra en comparación con la versión pura de WebSocket.

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 en torno al ciclo de solicitud y 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 una alta simultaneidad con un costo de rendimiento bajo. Estas arquitecturas generalmente están diseñadas en torno a subprocesos o a las E/S sin bloqueo.

Implementaciones del servidor

Versiones del protocolo

El protocolo de red (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, incluidas las notificaciones binarias. Además, Firefox será compatible con la versión 11 y Internet Explorer con la versión 10. Puedes seguir usando versiones anteriores del protocolo, pero no se recomienda, ya que se sabe que son vulnerables. Si tienes implementaciones de servidor para versiones anteriores del protocolo WebSocket, te recomendamos que lo actualices a la versión más reciente.

Casos de uso

Usa WebSocket cada vez 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 repensar la forma en que compilas tus aplicaciones del servidor con un nuevo enfoque en tecnologías como las filas de eventos. Estos son algunos ejemplos de casos de uso:

  • Juegos multijugador en línea
  • Aplicaciones de chat
  • Visor de deportes en vivo
  • Actualización en tiempo real de las transmisiones de redes sociales

Demostraciones

Referencias