martes, 27 de agosto de 2013

Real Time Web con Signal-R

Anteriormente en NoCompila hablamos sobre la web en tiempo real, eventos del lado del servidor, conexiones bidireccionales en web, etc.
En este post vamos a abandonar la teoría y a ponernos manos a la obra. Vamos a hacer la primera demo que recuerdo de SignalR para ver lo fácil que es de utilizar este framework.

Las conexiones web se gestionan mediante Hubs, SignalR analiza las capacidades del cliente y el servidor y decide la mejor técnica de comunicación que puede utilizar. De esta manera podemos abstraernos de la implementación concreta y centrarnos en la funcionalidad que queremos realizar.
En este ejemplo vamos a hacer que cada vez que se mueva retransmita al resto de usuarios su posición.
Lo primero es añadir las referencias a SignalR lo mejor que podemos hacer es añadirlas con Nuget. Al agregar la referencia se nos muestra un fichero txt donde nos indican que añadamos la siguiente línea en el evento Application_Start del global.asax para que funcione registrar la ruta donde se generarán los proxys javascript (parte de la magia)
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs();
        }
Así que lo primero que hay que hacer es extender la clase Hub de SignalR y comenzar a escribir los eventos del lado del servidor.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace SignalRSample
{
    public class MoveDivHub : Hub
    {
        public void MoveDiv(int x, int y)
        {
            Clients.Others.DivMoved(x, y);

        }
    }
}
Cada vez que sea invocado el método MoveDiv, el resto de clientes recibirán una invocación a DivMoved con las coordenadas correspondientes.
Con este poco código ya tenemos solucionado toda la parte del servidor. Ahora solo tenemos que escribir la parte del cliente, es decir, html, javascript y css. Después de añadir los ficheros de JQuery y JQueryUI en nuestro proyecto, comenzamos a escribir nuestro fichero javascript donde pondremos toda la lógica del cliente

$(function () {
    //Referencia a Hub del lado del servidor en MoveDivHub.cs, 
    //signalR por defecto utiliza lowerCamelCase asi que la primer letra va en minusculas
    var hub = $.connection.moveDivHub; 
    var div = $("#arrastrame");

    hub.client.divMoved = function (x, y) {
        div.css({left:x,top:y});
    }

    $.connection.hub.start().done(function () {
        div.draggable({
            drag: function () {
                //recuerda lowerCamelCase para a todos los nombres del hub
                hub.server.moveDiv(this.offsetLeft, this.offsetTop); 
            }
        });
    });
});
Como podéis ver usaremos draggable de JQueryUI para permitir que se mueva. Asignamos a divMoved una función que actualiza la posición del Div. y cuando iniciemos la conexión con el hub, aplicaremos draggable al div en el evento drag haremos una invocación al método moveDiv que está en el servidor pasándole la posición actual de nuestro div.

Lo único que nos queda es una página web donde poner la vista, algo sencillo como:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Ejemplo SignalR</title>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.3.min.js"></script>
    <script src="Scripts/jquery.signalR-1.1.2.min.js"></script>
    <script src="./signalr/hubs"></script>
    <script src="MoveDiv.js"></script>
    
    <style type="text/css">
        #arrastrame{
            width:100px;
            height:100px;
            background-color:#f00;
            border:solid;
            cursor:move;
        }
    </style>
</head>
<body>
    <div id="arrastrame"></div>
</body>
</html>

Y con esto todo listo. Listo, conexiones bidireccionales entre cliente y servidor y retransmisión de los datos a todos los clientes en muy pocas líneas. Podéis probar la demo haciendo click aquí (y de paso vemos si aguanta el pequeño servidor que he montado).
Una última reflexión: hacer un envío cada vez que se ejecuta el drag es una burrada ya que se genera demasiado tráfico de red. La demo que os he presentado funciona muy bien en local y en intranets, pero en internet no es viable hacer eso. En la demo publicada en internet, la sincronización se hace cada 100 milisegundos o al final del evento drag. Al hospedar la aplicación en mono con apache, aunque vuestros navegadores puedan utilizar websockets, SignalR utilizará longpolling, como podréis comprobar analizando el tráfico.
De la misma forma que cuando usamos los ORMs no debemos olvidar que tenemos por debajo una base de datos, tampoco debemos olvidar que aquí tenemos javascript y comunicaciones http que no son lo más rápido del mundo, así que debemos utilizarlas con cabeza.
Las posibilidades de esto son increíbles, por ejemplo el Visual Studio 2013 incorporará SignalR dentro de su plataforma de desarrollo web. Esto permitirá hacer cambios en caliente del html desde el editor y con un botón se podrán actualizar todos los navegadores que tengamos abiertos sin necesidad de pulsar f5 (y todo lo que eso conlleva) en cada uno de ellos. Me pregunto cuantos usos más veremos de esta tecnología.

1 comentario: