¿Qué son los Websockets?

En los últimos años, un nuevo tipo de comunicación comenzó a surgir en la web y en las aplicaciones móviles, llamado websockets. Este protocolo ha sido largamente esperado y finalmente fue estandarizado por el IETF en 2011, allanando el camino para su uso generalizado.

Este nuevo protocolo abre una línea de comunicación mucho más rápida y eficiente para el cliente. Al igual que HTTP, los websockets se ejecutan sobre una conexión TCP, pero son mucho más rápidos porque no tenemos que abrir una nueva conexión para cada vez que queramos enviar un mensaje, ya que la conexión se mantiene viva durante todo el tiempo que el servidor o el cliente quieran.

Mejor aún, como la conexión nunca muere, por fin tenemos a nuestra disposición una comunicación full-duplex, lo que significa que podemos empujar datos al cliente en lugar de tener que esperar a que éste pida datos al servidor. Esto permite que los datos se comuniquen de ida y vuelta, lo que es ideal para cosas como aplicaciones de chat en tiempo real, o incluso juegos.

¿Cómo funcionan los Websockets?

En su esencia, un websocket es sólo una conexión TCP que permite la comunicación full-duplex, lo que significa que cualquier lado de la conexión puede enviar datos al otro, incluso al mismo tiempo.

Para establecer esta conexión, el protocolo realmente inicia el handshake como una solicitud HTTP normal, pero luego se «actualiza» utilizando la cabecera HTTP de solicitud de actualización, así:

El servidor entonces devuelve una respuesta HTTP 101 «Switching Protocols», reconociendo que la conexión se va a actualizar. Una vez que esta conexión se ha realizado, cambia a un protocolo binario bidireccional, momento en el que se pueden enviar los datos de la aplicación.

Todo lo que tiene que hacer el protocolo para mantener la conexión abierta es enviar algunos paquetes ping/pong, que indican al otro lado que siguen ahí. Para cerrar la conexión, se envía un simple paquete «close connection».

Algunos ejemplos de Websocket

De las muchas y diferentes librerías de websocket para Node.js que tenemos disponibles, he optado por utilizar socket.io a lo largo de este artículo porque parece ser la más popular y es, en mi opinión, la más fácil de usar. Mientras que cada biblioteca tiene su propia API única, también tienen muchas similitudes, ya que todas están construidas sobre el mismo protocolo, por lo que espero que seas capaz de traducir el código de abajo a cualquier biblioteca que quieras usar.

Para el servidor HTTP, voy a utilizar Express, que es el servidor Node más popular que hay. Ten en cuenta que también puedes usar simplemente el módulo http si no necesitas todas las características de Express. Aunque, dado que la mayoría de las aplicaciones utilizarán Express, eso es lo que usaremos también.

Nota: A lo largo de estos ejemplos he eliminado gran parte del código boilerplate, por lo que parte de este código no funcionará fuera de la caja. En la mayoría de los casos puedes consultar el primer ejemplo para obtener el código boilerplate.

Establecer la conexión

Para que se establezca una conexión entre el cliente y el servidor, el servidor debe hacer dos cosas:

  1. Engancharse al servidor HTTP para manejar las conexiones websocket
  2. Servir la librería del cliente socket.io.js como un recurso estático
  3. En el código de abajo, puedes ver que el punto (1) se hace en la 3ª línea. El punto (2) lo hace por ti (por defecto) la librería socket.io y se sirve en la ruta https://stackabuse.com/socket.io/socket.io.js. Por defecto, todas las conexiones y recursos de websocket se sirven dentro de la ruta /socket.io.

    Servidor

    El cliente también necesita hacer dos cosas:

    1. Cargar la librería desde el servidor
    2. Llamar .connect() a la dirección del servidor y a la ruta del websocket
    3. Cliente

      <script src="https://stackabuse.com/socket.io/socket.io.js"></script><script> var socket = io.connect('/');</script>

      Si navegas en tu navegador hacia e inspeccionas las peticiones HTTP entre bastidores utilizando las herramientas de desarrollo de tu navegador, deberías ser capaz de ver el handshake que se está ejecutando, incluyendo las peticiones GET y la respuesta resultante de HTTP 101 Switching Protocols.

      Enviando datos del servidor al cliente

      Bien, ahora pasamos a algunas de las partes más interesantes. En este ejemplo vamos a mostrar la forma más común de enviar datos del servidor al cliente. En este caso, vamos a enviar un mensaje a un canal, al que se puede suscribir y recibir el cliente. Así, por ejemplo, una aplicación cliente podría estar escuchando en el canal ‘announcements’, que contendría notificaciones sobre eventos de todo el sistema, como cuando un usuario se une a una sala de chat.

      En el servidor esto se hace esperando a que se establezca la nueva conexión, y luego llamando al método socket.emit() para enviar un mensaje a todos los clientes conectados.

      Servidor

      io.on('connection', function(socket) { socket.emit('announcements', { message: 'A new user has joined!' });});

      Cliente

      Envío de datos de cliente a servidor

      ¿Pero qué haríamos cuando queremos enviar datos en sentido contrario, de cliente a servidor? Es muy similar al último ejemplo, utilizando los dos métodos socket.emit() y socket.on().

      Servidor

      Cliente

      Contando usuarios conectados

      Este es un buen ejemplo para aprender ya que muestra algunas características más de socket.io (como el evento disconnect), es fácil de implementar y es aplicable a muchas webapps. Utilizaremos los eventos connection y disconnect para contar el número de usuarios activos en nuestro sitio, y actualizaremos todos los usuarios con el recuento actual.

      Servidor

      Cliente

      Una forma mucho más sencilla de hacer un seguimiento del recuento de usuarios en el servidor sería simplemente usar esto:

      var numClients = io.sockets.clients().length;

      Pero aparentemente hay algunos problemas en torno a esto, por lo que es posible que tengas que hacer un seguimiento del recuento del cliente tú mismo.

      Salas y espacios de nombres

      Es probable que a medida que tu aplicación crezca en complejidad, necesites más personalización con tus websockets, como enviar mensajes a un usuario o conjunto de usuarios específico. O tal vez quieras una separación estricta de la lógica entre las diferentes partes de tu aplicación. Aquí es donde las salas y los espacios de nombres entran en juego.

      Nota: Estas características no son parte del protocolo de websocket, sino que se añaden encima por socket.io.

      Por defecto, socket.io utiliza el espacio de nombres raíz (/) para enviar y recibir datos. Programáticamente, puedes acceder a este espacio de nombres a través de io.sockets, aunque muchos de sus métodos tienen accesos directos en io. Así que estas dos llamadas son equivalentes:

      io.sockets.emit('stats', { data: 'some data' });io.emit('stats', { data: 'some data' });

      Para crear tu propio espacio de nombres, sólo tienes que hacer lo siguiente:

      Además, el cliente debe conectarse a tu espacio de nombres explícitamente:

      <script src="https://stackabuse.com/socket.io/socket.io.js"></script><script> var socket = io('/stackabuse');</script>

      Ahora cualquier dato que se envíe dentro de este espacio de nombres estará separado del espacio de nombres por defecto /, independientemente del canal que se utilice.

      Vayamos aún más lejos, dentro de cada espacio de nombres se pueden unir y dejar «salas». Estas salas proporcionan otra capa de separación por encima de los espacios de nombres, y dado que un cliente sólo puede añadirse a una sala en el lado del servidor, también proporcionan algo de seguridad adicional. Así que si quieres asegurarte de que los usuarios no están fisgoneando ciertos datos, puedes usar una sala para ocultarlos.

      Para que te añadan a una sala, debes .join()la:

      io.on('connection', function(socket){ socket.join('private-message-room');});

      Después, desde ahí puedes enviar mensajes a todos los que pertenezcan a la sala determinada:

      io.to('private-message-room').emit('some event');

      Y por último, llama a .leave() para dejar de recibir mensajes de eventos de una sala:

      socket.leave('private-message-room');

      Conclusión

      Esta es sólo una librería que implementa el protocolo websockets, y hay muchas más por ahí, todas con sus propias características y puntos fuertes. ¡Te aconsejo que pruebes algunas de las otras (como node-websockets) para que te hagas una idea de lo que hay por ahí.

      Con sólo unas pocas líneas, puedes crear algunas aplicaciones bastante potentes, así que tengo curiosidad por ver lo que se te ocurre!

Categorías: Articles

0 comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *