Antes de..

  • Internet se ha creado en gran parte a partir del llamado paradigma solicitud/respuesta de HTTP.

  • En 2005, AJAX empezó a hacer que Internet pareciera más dinámico.

  • Existen Tecnologías que permiten al servidor enviar datos al cliente en el mismo momento que detecta que hay nuevos datos disponibles. Conocidas como "Push" o "Comet".

  • Actualmente se usa "Long Polling" un truco que sirve para mantener una conexión HTTP con el servidor que permanece abierta hasta que se envíe una respuesta.

Pero ...!

Esas soluciones son ineficientes

El problema: el exceso del HTTP , lo que no las hace aptas para aplicaciones de baja latencia. Piensa por ejemplo en los juegos multijugador de tiro en primera persona del navegador o en cualquier otro juego online con un componente en tiempo real.

Mejor pensemos en algo :

  • Que responda sin ser invocado
  • Tanto cliente como servidor

  • Una Solución "Real Time"

¿Real Time?

Si, ya no queremos esperar más ni usar trucos extraños. Gracias a el auge de web moderna tenemos soluciones como Node.js :


¿WebSockets?

La especificación WebSocket define un API* que establece conexiones "socket" entre un navegador web y un servidor.

Dicho con otras palabras: existe una conexión persistente entre el cliente y el servidor, y ambas partes pueden empezar a enviar datos en cualquier momento.

Actualmente existe WebSocket nativo en navegadores como Google Chrome, Firefox, Opera.

Existen librerias para implpentarlo en Objective-C,. NET, Ruby, Java, Node.js, ActionScript, entre otros.

* Interfaz de programación de aplicaciones o API es el conjunto de funciones y procedimientos que ofrece cierta biblioteca para ser utilizado por otro software como una capa de abstracción
The goal of this technology is to provide a mechanism for browser-based applications that need two-way communication with servers that doesn't rely on opening multiple HTTP connection.
The WebSockect Protocol
RFC 6455

Wow, Increible ¿Entonces como puedo hacer todo eso?


Veamos la API de WebSockets

WebSockets en el Cliente

Introducción

La API de WebSocket es bastante simple. Del lado cliente, tenemos que crear una instancia de la clase WebSocket del cliente . Esta clase expone varios eventos interesantes que requieren de controladores apropiados:

        var wsUri = " ws://echo.websocket.org/";
        socket = new WebSocket(wsUri);
        socket.onopen = function(evt) { onOpen(evt) };
        socket.onmessage = function(evt) { onMessage(evt) };
        socket.onclose = function(evt) { onClose(evt) };
        socket.onerror = function(evt) { onError(evt) };

Definición : Eventos

  • El evento onopen se activa cuando se establece la conexión.
  • El evento onmessage se activa siempre que un cliente recibe un mensaje del servidor.
  • El evento onclose se activa cuando la conexión se cierra.
  • Por último, onerror se activa cada vez que ocurre un error.

Creación de WebSockets (Cliente)

Introducción

Para abrir una conexión WebSocket, sólo tenemos que ejecutar el constructor WebSocket, que toma como parámetro la URL del socket a abrir.

    var socket = new WebSocket('ws://domain.xx/tweets');

También existe un protocolo wss:// para conexiones WebSocket seguras, de la misma forma que se utiliza https:// para las conexiones HTTP seguras.

Comunicación con servidor

Introducción

Cuando se establece una conexión con el servidor (cuando el evento open se activa), se puede empezar a enviar datos al servidor con el método send a través del socket creado.

        // Envió de un nuevo Tweet
        socket.send(" Hola , Estoy usando WebSockets");

De la misma forma, el servidor puede enviarnos mensajes en cualquier momento. Cada vez que esto ocurra, se activa el evento onmessage. Los datos enviados por el servidor se encuentran en la propiedad data del objeto event.

socket.onmessage = function(event) {
        var data = JSON.parse(event.data);
        if (data.action == 'joined') {
            initiliseChat();
        } else {
            showNewMessage(data.who, data.text);
        }
   });

Comunicación con servidor

Introducción

El API incorpora además dos eventos que se disparan cuando el socket se abre y está listo, y cuando éste se va a cerrar:

       socket.onopen  = function(e){ log("Welcome-status"+this.readyState);};
       socket.onclose = function(e){ log("Disconnected-status"+this.readyState); };

WebSockets en el servidor

Introducción

Al utilizar los WebSocket, se crea un patrón de uso completamente nuevo para las aplicaciones de servidor. Aunque las pilas de servidor tradicionales como LAMP están diseñadas a partir del ciclo de solicitud-respuesta de HTTP, a menudo dan problemas si hay muchas conexiones WebSocket abiertas.

Mantener un gran número de conexiones abiertas de forma simultánea requiere una arquitectura capaz de recibir un alto nivel de concurrencia sin consumir muchos recursos.

¿Que librerias tengo para..

Implementaciones en el servidor ?

Algunas Implicaciones

Proxys, CORS, Soporte

¿ Donde lo puedo usar ?

Browser Support

Today, most transparent proxy servers will not yet be familiar with the Web Socket protocol and these proxy servers will be unable to suppor the Web Socket protocol
Peter Lubbers
In a 2010 InfoQ article

Cross-origin Resource Sharing (CORS)

Lo nuevo

Las comunicaciones de origen cruzado son un protocolo moderno creado directamente para WebSocket. Aunque sigue siendo necesario que te asegures de que solo te comunicas con clientes y servidores de confianza, WebSocket permite la comunicación entre las partes de cualquier dominio.

El servidor decide si desea poner su servicio a disposición de todos los clientes o solo de los que están alojados en un grupo de dominios bien definidos.

Sokects ! usalo para tus Proyectos..

Real Time !!

Utiliza WebSocket siempre que necesites una conexión casi a tiempo real y de latencia baja entre el cliente y el servidor.

Si , esto podría significar tener que replantearte cómo has desarrollado tus aplicaciones de servidor, adoptando un nuevo enfoque en tecnologías como las colas de eventos. Estos son algunos ejemplos de casos prácticos:

  • Juegos online multijugadores
  • Pagos con monedas Criptograficas (Bitcoin)
  • Rotativos de información deportiva
  • Actualizaciones en tiempo real de las actividades de tus amigos

Veamos un poco el protocolo..



WebSocket

Protocol Details

  • TCP-based protocol
  • HTTP used solely for upgrade request (Status Code 101)
  • Bi-directional, full-duplex
  • Data frames can be text(UTF-8) or arbitrary Binary data

WebSocket

Protocol Schemes (URI)

  • Unencrypted: ws://
  • Encryted: wss://

Entonces, Contruyamos algo Real Time !!

Con Sockets y node !

Creamos la nuestra app


    $ express -e -c stylus draw_app
    $ cd draw_app && npm install   

Instalamos las dependencias


$ npm install --save socket.io

Lo que debe hacer el cliente


  • Crea un espacio para los dibujos
  • Muestra mis amigos conectados
  • Establece conexión con el servidor
  • Mantiene el estado del usuario
  • Comunica al servidor los movimientos del usuario
  • Traduce los movimientos de otros a dibujos
  • Elimina sesiones que expiran

Code, Code :D

<div id="cursors">
<!-- Aqui pondremos los cursores de nuestros amigos -->
<div>
<canvas id="paper" width="1900" height="1000">
Tu navegador debe soportar canvas
</canvas>
<hgroup id="instructions">
<h1>¡Dibujemos!</h1>
<h2>Dibuja en cualquier lugar con tu cursor</h2>
<h3>Si quieres un canvas nevo simplemente refresca la página</h3>
</hgroup>
body {
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif
}
* {
  margin:0;
  padding:0;
}
#cursors {
  position:relative;
}
.cursor {
  position:absolute;
  width:15px;
  height:22px;
  background:url('../images/pointer.png') no-repeat -4px 0;
}
canvas {
  background: #ddd;
}

Inicializacíon y estado


  var url = 'http://localhost:3000';

  // cache de objetos de jQuery
  var doc = $(document);
  var win = $(window);
  var canvas = $('#paper');
  var instructions = $('#instructions');
  var ctx = canvas[0].getContext('2d');

  // id único para la session
  var id = Math.round($.now()*Math.random());

  // inicializamos el estado
  var drawing = false;
  var clients = {};
  var cursors = {};
  var prev = {};
  var lastEmit = $.now();
  // abrimos la conexion


Eventos locales - mousedown


function mousedownHandler(e) {
    e.preventDefault();
    drawing = true;
    prev.x = e.pageX;
    prev.y = e.pageY;

    // escondemos las instrucciones
    instructions.fadeOut();
  }

Eventos locales - mousemove


function mousemoveHandler(e) {
    if($.now() - lastEmit > 30){
      var movement = {
        'x': e.pageX,
        'y': e.pageY,
        'drawing': drawing,
        'id': id
      };
      socket.emit('mousemove', movement);
      lastEmit = $.now();
    }

    if(drawing){

      drawLine(prev.x, prev.y, e.pageX, e.pageY);

      prev.x = e.pageX;
      prev.y = e.pageY;
    }
  }

Eventos remotos - move


function moveHandler(data) {
    if(! (data.id in clients)){
      // le damos un cursor a cada usuario nuestro
      cursors[data.id] = $('
').appendTo('#cursors'); } // movemos el cursor a su posicion cursors[data.id].css({ 'left' : data.x, 'top' : data.y }); if(data.drawing && clients[data.id]){ drawLine(clients[data.id].x, clients[data.id].y, data.x, data.y); } // actualizamos el estado clients[data.id] = data; clients[data.id].updated = $.now(); }

Adjuntemos los eventos


  socket.on('move', moveHandler);
  canvas.on('mousedown', mousedownHandler);
  doc.on('mousemove', mousemoveHandler);

  doc.bind('mouseup mouseleave',function(){
    drawing = false;
  });

Y Dibujemos !!


  function drawLine(fromx, fromy, tox, toy){
  ctx.moveTo(fromx, fromy);
  ctx.lineTo(tox, toy);
  ctx.stroke();
  }

¿Qué hace el servidor?


  • Establecer conexiones http
  • Servir recursos estaticos (HTML, CSS, JS)
  • Establecer conexiones de sockets
  • Transmitir eventos de movimiento a clientes

Dependencias


  var express = require('express');
  var http = require('http');
  var path = require('path');
  

servir recursos estaticos, CSS y JS


  app.set('views', path.join(__dirname, 'views'));
  app.set('view engine', 'ejs');
  app.use(require('stylus').middleware(path.join(__dirname, 'public')));
  app.use(express.static(path.join(__dirname, 'public')));
  

Rutas - html


  app.get('/', function(req, res){
  res.render('index', { title: 'Dibujemos' });
  });
  

Establecer conexiones sockets y http


  io = io.listen(server);
  server.listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
  });
  

Adjuntarnos a eventos de sockets y transmitir


  // Escuchamos conexiones entrantes
  io.sockets.on('connection', function (socket) {
    // Detectamos eventos de mouse
    socket.on('mousemove', function (data) {
      // transmitimos el movimiento a todos los clientes conectados
      socket.broadcast.emit('move', data);
    });
  });
  

$ node app.js

Corramos la app

Pólvora, agua caliente y WebSockets

Conclusión

Muchas personas han dicho que WebSockets es el invento más útil después de la pólvora y el agua caliente. Después de comprender WebSockets, solo queda preguntarnos cómo el mundo del software logró prosperar previamente. WebSockets resulta útil para varias aplicaciones, pero no para cualquier aplicación.

Cualquier aplicación donde la mensajería instantánea sea un elemento clave resulta un contexto potencial en el que puede plantearse seriamente la posibilidad de crear un servidor WebSocket con un conjunto de clientes (web, móviles o incluso de escritorio).

Los juegos y las aplicaciones con fuentes en directo corresponden a otros ámbitos de la industria que se beneficiarán enormemente del protocolo WebSocket. ¡Sí, WebSockets definitivamente es lo mejor después del agua caliente!

- MDN MAGAZINE

<Thank You!>