RSS Feed

WebSockets y SocketIO

18

noviembre 22, 2012 by - @pjnovas

Si todavía no arrancaste a ver la magia del protocolo WS (web socket) este post intenta meterte en tema.

Qué es WebSocket?


WebSocket es un protocolo nuevo para la web bajo TCP, por el cual, a diferencia de la conexión que venimos usando bajo HTTP, este es bi-direccional, que significa esto?, hoy por hoy venís usando una conexión en una sola dirección, pedís al servidor y esperas la respuesta (o viceversa con ServerEvents). Pero con web sockets el servidor te habla también, te puede llamar y mandar un mensaje en cualquier momento.



Esto es genial, ya que las posibilidades de magia en un sito web aumentan considerablemente, pero hay que tener en cuenta algunas cosas, ya que son Web Sockets:

  • Se inicia con un handshake HTTP, por lo que si no hay HTTP no hay WebSockets
  • Tanto servidor como cliente tienen que soportarlo (para eso tenemos a HTML 5 en cliente y SocketIO en el servicor :P )
  • Sólo podemos transmitir texto/ JSON (a diferencia del TCP convencional en el que podemos transmitir streams de bytes)
  • La conexión TCP va por el puerto 80 (lo cual está bueno en algunos casos donde tenemos firewalls)
  • Así como tenemos HTTPS (seguros), podemos tener WSS jeje, pero no todos los navegadores que soportan WS, también soportan WSS


Cómo funciona?

Para iniciar una conexión con el protocolo WS primero el cliente le pide al servidor que quiere iniciar esta conexión (handshake: se pasan unos secretitos para validarse), el servidor responde un “dale para adelante” y a partir de ahí dejan de utilizar HTTP y pasan a WS.
Aparte de darnos está posibilidad de una conexión bi-direccional, nos ahorramos el “payload”, es decir, en estas transmiciones que hacemos por el protocolo ws ya no tenemos el HTTP header y toda la data de “info” para que el servidor sepa que le enviamos y nosotros que recibimos, qué tipo de conexión usamos, etc.
Tengamos en cuenta que transmitimos texto y nos quedamos enganchados al servidor hasta que nosotros como clientes decidamos que no queremos estar mas comunicados.

Ahora que ya sabemos de que se trata, vamos a utilizarlo.


SocketIO es una biblioteca que nos facilita increíblemente el uso de web sockets en NodeJS.
Así que arranquemos a configurar:

Instalamos el paquete NPM de SocketIO y ya que estamos instalamos el de ExpressJS para nuestro servidor Web:

npm install socket.io
npm install express

Creamos un server.js donde vamos a levantar nuestro servidor Express con soporte para WebSocket

//referenciamos a expressJS
var app = require('express')()
  // creamos un web server
  , server = require('http').createServer(app)
  // y le agregamos le agregamos socketIO
  , io = require('socket.io').listen(server);

// ponemos en escucha nuestro server Express con WebSocket
server.listen(80);

// agregamos una ruta inicial para retornar un index.html
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

// nos suscribimos al evento de socketIO cuando 
// un cliente se conecta por WebSockets
io.sockets.on('connection', function (socket) {

  // este callback va a ser llamado cuando tenemos
  // un nuevo cliente y en el argumento 'socket'
  // vamos a tener nuestro 'enganche' a ese cliente  

  // apenas se conecta, le mandamos un mensaje
  // de bienvenida haciendo un 'emit' con un nombre
  // para el mensaje y un json con los datos
  socket.emit('bienvenida', { digo: 'Hola cliente WS!' });

  // nos suscribimos a un mensaje que nos puede 
  // enviar el cliente.
  socket.on('quiero un random', function (cada_cuanto) {

    setInterval(function(){
      var rnd = Math.floor((Math.random()*1000)+1);
      socket.emit('toma un random', { numero: rnd })
    }, cada_cuanto);

  });
});

Ahora creamos un index.html que es el que retorna el servidor con la conexión a WebSocket

<!DOCTYPE html>
<html>
  <head>
    <!-- Este script no existe!, y está bien que así sea, ya que lo genera 
         SocketIO automáticamente al recibir el pedido del archivo -->
    <script src="/socket.io/socket.io.js" type="text/javascript"></script>

    <script type="text/javascript">
      // Nos conectamos al WebSocket
      var socket = io.connect('http://localhost');

      // Nos suscribimos al mensaje de bienvenida 
      // que creamos en el servidor
      socket.on('bienvenida', function (bienvenida) {
        document.write(bienvenida.digo);
        
        // Una vez que nos saluda el servidor
        // Le enviamos un mensaje pidiendo un random 
        // cada 5 segundos (5000 milisegundos)
        socket.emit('quiero un random', 5000);
      });

      // Por otro lado nos suscribimos al mensaje del
      // del servidor para el número random que nos va a enviar
      socket.on('toma un random', function (data) {
        console.log(data.numero);
      }); 
    </script>
  </head>
  <body>
    <!-- acá toda la magia en HTML -->
  <body/>
</html>

Lo que nos queda es correr el servidor web

node server.js

Abrimos un navegador y llamamos a http://localhost y listo! (abrí la consola para ver las llamadas del servidor con los randoms :) ).

Al principio expliqué que tanto el cliente como el servidor deben soportar web sockets, tenemos que tener en cuenta que es necesario HTML5 con web sockets, te dejo este LINK con el detalle de los navegadores que lo soportan. Fuera de eso tené en cuenta que SocketIO se encarga de que la conexión funcione sin importar el navegador, esto lo hace intentando con otros mecanismos de transporte (XHR Pooling, Flash Sockets, etc.). No va a ser con web sockets pero va a funcionar como si lo fuera :)



Te dejo el código del ejemplo – ejemplo.zip

Toma este código como un ejemplo muy simple y básico de SocketIO, se pueden hacer muchísimas cosas más, como armar rooms o agrupar clientes por ruta de URI, emitir mensajes a todos o sólo al que le habló al servidor, etc.
Te recomiendo que veas mas ejemplos en el sitio de socket.io: how to use

Links Útiles


  • Omar Guntaue

    Excelente post

  • Nicolas

    Un post mejor que el otro… websockets por lo poco que lo he trabajado, es LA posta….

  • http://twitter.com/maxidr Maxi Dello Russo

    Otro excelente post!!!

  • maro

    groso! directo a fav ;)

  • lucho

    muy bueno! tengo una pregunta: si quisiera enviar mensajes entre clientes. si o si tengo que pasar por el server? sos grosso

    • pjnovas

      Si, definitivamente. Para entrar en el protocolo WS tenes que primero pasar por un HTTP, así que si o si la comunicación es entre un cliente y un servidor.
      Por lo que ambos clientes van a conectarse por WS a un server y este va a comunicarlos ;)
      Saludos!

    • matiasarriola

      Si quisieras enviar mensajes entre browsers directamente, deberías usar otra tecnología. Hace rato existen soluciones medias locas que necesitan flash en algún punto. Sino, ya implementado por chrome y firefox, existe WebRTC. Si lo que querés es enviar cualquier tipo de data entre dos browsers podés llegar a usar un RTCDataChannel y su funcionamiento a nivel API es muy parecido al de websockets. Esto es bastante nuevo y no me metí bien en tema así que no sé que problemas pueden llegar a existir, etc. pero en cuanto lo pueda probar les haré saber
      Saludos

      • pjnovas

        WebRTC?, ya está?, eso es “Real Time Games” posta! … me pongo de la nuca! jeje

    • Ramos Portillo Byron

      haha usa el metodo io.socket.emit(“nombre de la funcion que maneja el mensaje en el cliente”, “parametros para la funcion”);

  • pjnovas

    No tanto, ahí está solo para Chrome v26+ y Firefox no tiene versión todavía, le falta un rato :P

  • pjnovas

    Bueno, en un mes cambian mucho las cosas jaja, les dejo link desde Mozilla :)
    https://hacks.mozilla.org/2013/03/webrtc-data-channels-for-great-multiplayer/

  • chicharooon

    Hola gente se que no estoy un foro pero igualmente les comparto una duda que tengo (que expresé aquí hace un tiempo http://goo.gl/zNfCZ luego de haber googleado el tema) ¿como envío, con socket.io, mensajes entre usuarios (como por ejemplo el chat de facebook donde puedo elegir un usuario y comunicarme privadamente con el). Entiendo que con el método emit() enviamos “todos a todos”, con broadcast() enviamos “uno a todos”, me falta saber como enviar “uno a uno”. Desde ya muchas gracias.
    Tremendo blog y gran artículo, los sigo desde hace tiempo. Sigan así. Felicitaciones.

    • pjnovas

      Buenas!, la forma mas fácil que se me ocurre en este momento es que crees un room con los dos usuarios que se van a mandar mensajes privados, de esa manera esos usuarios mandan un broadcast a la room en la que están y solo ellos leen los mensajes.

      Podrías crear un room con los ids de ambos, por ejemplo 02_04.

      Te dejo mas info sobre las rooms: https://github.com/LearnBoost/socket.io/wiki/Rooms

      Te va a quedar algo como:

      io.sockets.in(userA.id + '_' + userB.id).emit('chat', { msj: 'todo bien?' });

      • chicharooon

        Muchas gracias!! ya mismo lo pruebo :)

  • pelias

    Hola. Para poder utilizar websocket, ¿necesitamos un hosting que tenga instaladas las bibliotecas verdad? ¿Alguien sabe de algún hosting gratuito con estas características?

    • matiasarriola

      Hola, generalmente los hostings gratuitos no te permiten el uso de websockets. En su momento nodejitsu lo permitía, pero hasta donde yo sé ahora empezaron a cobrar.

      Lo que podés hacer es usar una biblioteca que te dé una abstracción copada de la comunicación, como socket.io, y con la opción gratuita de heroku va a funcionar casi sin modificaciones, aunque no realmente con websockets, sino que por detrás con xhr polling (https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku.).

      Me sumo a la consulta de pelias :)

      • pelias

        Muchísimas gracias, me ha ayudado mucho.

        • pelias

          En fin si hay que pagar algo por un buen servicio y no lo encuentro por otro sitio gratis, estoy dispuesto. Aunque siempre es mejor probar primero si funciona de manera gratuita porque a veces he subido script php a servidores que funcionaban a la perfección en el mio y sin embargo no funcionan en aquellos.