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

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

La Web se compiló en gran medida en torno al llamado paradigma de solicitud y 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 dirigía toda la comunicación HTTP, lo que requería la interacción del usuario o sondeos periódicos para cargar los datos nuevos desde el servidor.

Las tecnologías que permiten que el servidor envíe datos al cliente en el momento preciso en que sabe que hay nuevos datos disponibles existen desde hace bastante tiempo. Se llaman "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 un sondeo largo, el cliente abre una conexión HTTP al servidor que la mantiene abierto hasta que se envía la respuesta. Cada vez que el servidor tiene datos nuevos, envía la respuesta (otras técnicas son Flash y las solicitudes XHR multiparte, y los llamados archivos HTML). Los sondeos largos y las demás técnicas funcionan bastante bien. Las usas todos los días en aplicaciones como el chat de Gmail.

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

Presentación de WebSocket: el ingreso de los 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, existe una conexión persistente entre el cliente y el servidor, y ambas partes pueden comenzar a enviar datos en cualquier momento.

Primeros pasos

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 la ws:. Este es el nuevo esquema de URL para las conexiones de WebSocket. También hay wss: para la conexión segura de WebSocket, de la misma manera que se usa https: para las conexiones HTTP seguras.

Adjuntar de inmediato algunos controladores de eventos a la conexión te permite saber si la conexión se abre, si se reciben mensajes entrantes o si hay 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 acepta solo uno de los subprotocolos pasados del 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 subprotocolos registrados en el registro de IANA. Actualmente, solo hay un nombre de subprotocolo (jabón) 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);
};

Comunícate 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 mediante el método send('your message') en el objeto de conexión. Solía admitir solo cadenas, pero con la especificación más reciente ahora 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);

De igual manera, el servidor puede enviarnos mensajes en cualquier momento. Cuando 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 según 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 como "blob" o "arraybuffer". El formato predeterminado es "blob". (No tienes que 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 las extensiones del objeto WebSocket después del evento abierto. No hay especificaciones de extensiones publicadas oficialmente a partir de febrero de 2012.

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

Comunicación de origen cruzado

Dado que es un protocolo moderno, la comunicación de origen cruzado está integrada en WebSocket. Si bien debes asegurarte de comunicarte solo con los clientes y servidores en los que confías, WebSocket permite la comunicación entre las partes de 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 lo que media las conexiones HTTP en la mayoría de las redes empresariales. El protocolo WebSocket usa el sistema de actualización 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 perderán 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 :)

Usar WebSockets hoy

WebSocket es una tecnología reciente y no se implementa por completo en todos los navegadores. Sin embargo, puedes usar WebSocket actualmente 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 viene con un cliente y una implementación de servidor del protocolo, e incluye resguardos (socket.io aún no admite mensajería binaria desde febrero de 2012). También existen soluciones comerciales, como PusherApp, que se pueden integrar fácilmente en cualquier entorno web al proporcionar 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

Cuando usas WebSocket, se crea un patrón de uso completamente nuevo para las aplicaciones del servidor. Si bien las pilas de servidor tradicionales como LAMP están diseñadas en torno al ciclo de solicitud/respuesta HTTP, a menudo no funcionan bien con una gran cantidad de conexiones de WebSocket abiertas. Mantener una gran cantidad de conexiones abiertas al mismo tiempo requiere una arquitectura que reciba una alta simultaneidad a 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 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 y con Internet Explorer en la versión 10. Aún puedes usar versiones de protocolo más antiguas, 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 cómo compilar 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
  • Televisor de deportes en vivo
  • Actualización en tiempo real de las transmisiones de redes sociales

Demostraciones

Referencias