Creando aplicaciones web en tiempo real con SignalR

Es posible que alguna vez nos hayamos preguntado cómo construir una aplicación web en tiempo real. Lo primero que probablemente haremos será realizar una búsqueda en Internet y veremos que existen muchísimas tecnologías basadas en muchos lenguajes, y no sabremos siquiera por dónde empezar.

Si estás en una situación similar, no te preocupes, porque has venido al sitio adecuado. En este post vamos a ver cómo podemos comenzar a desarrollar una aplicación en tiempo real desde cero, sin tener conocimientos previos del tema.

Para ello vamos a utilizar SignalR. No vamos a compararlo con otras tecnologías que ya existan, ni vamos a ver en qué es mejor o peor que otras. Pero lo que sí te aseguramos es que una vez lo pruebes va a ser difícil que quieras cambiar.

¿Qué es SignalR?

SignalR es una librería creada para desarrolladores de ASP.NET que facilita el proceso de añadir funcionalidades en tiempo real a aplicaciones web, es decir, la capacidad del servidor de enviar información al cliente sin tener que esperar a que el cliente realice una petición.

Ejemplos de aplicaciones web en tiempo real pueden ser chats, monitorización, aplicaciones colaborativas o incluso juegos.

¿Y cómo funciona? SignalR001

 

SignalR ofrece una API para crear llamadas remotas (RPC) desde el servidor a funciones Javascript existentes en el cliente. No tenemos que encargarnos de gestionar la conexión, ya que SignalR lo hace automáticamente. Podemos enviar mensajes a un cliente específico o a todos los conectados al servidor.

SignalR se basará en Websocket cuando esté disponible. Cuando no sea el caso, utilizará otra tecnología disponible para funcionalidades en tiempo real, como puede ser Server Sent Requests, Forever Frame o Ajax long polling. En función del navegador en el que se ejecute la aplicación, se escogerá un método de transporte u otro, pero es un proceso totalmente transparente para nosotros, sin que sea necesario tener conocimientos de estos transportes ni de cómo debemos implementar nuestro código en función del navegador que utilicemos.

Para implementar una aplicación SignalR tendremos lo que denominamos un Hub y un Hub proxy.

Un Hub/Hub proxy no es más que un medio que permite al cliente realizar llamadas a métodos servidor y viceversa, como si esos métodos se estuvieran llamando en local.

El Hub estará en la parte del servidor, y expondrá los métodos disponibles al cliente. El Hub proxy estará en el lado del cliente, y expondrá los métodos disponibles al servidor.

Comencemos…

Nada mejor que ponerse manos a la obra para aprender y ver cómo funcionan las cosas, así que vamos a ello.

Vamos a realizar una pequeña aplicación que consistirá en un chat. Según entren usuarios, se les irá pidiendo su nombre de usuario y podrán chatear en una sala habilitada para todos.

Vamos a crear un proyecto web nuevo, de ASP.NET escogiendo la plantilla vacía.

SignalR002

Una vez lo tengamos, tendremos que añadir las librerías necesarias para utilizar SignalR. Para ello, hacemos click derecho en el proyecto, le damos a Manage Nuget Packages, buscamos SignalR e instalamos el paquete Microsoft.AspNet.SignalR, tal como se indica en la imagen.

SignalR003

Una vez hecho esto, vamos a comenzar a crear la parte del servidor.

Lo primero que tendremos que hacer es registrar la ruta que los clientes utilizarán para conectarse al Hub, que por defecto será “ /signalr” (puede cambiarse si fuese necesario). Esto se hace en el fichero Startup.cs (si al crear el proyecto no se ha generado ninguno, créalo manualmente). Dentro de la clase Startup, llamaremos a MapSignalR para registrar dicha ruta. El código nos debe quedar como podemos ver en la siguiente imagen.

SignalR004

Tras ello crearemos el Hub en sí.

Creamos una carpeta llamada Hubs y dentro creamos una clase ChatHub, con el siguiente código:

SignalR005

Como vemos, la clase hereda de Hub, y podemos empezar a añadirle métodos que podrán ser llamados desde el lado del cliente. Todos los métodos deben ser públicos y pueden retornar valores que llegarán al cliente en formato JSON.

Vamos desglosar aquí el código que hemos puesto en la clase para ver qué realizan.

SignalR006

En primer lugar, tenemos un listado para almacenar los usuarios que se van a ir conectando. De cada uno almacenaremos su nombre de usuario y su ID de conexión.

A continuación, nos encontramos con los métodos que expone el Hub. Connect y Send se llamarán para almacenar los usuarios que se vayan agregando y para mandar mensajes a todas las personas conectadas respectivamente.

Por otro lado, el método onDisconnected() es una sobrecarga de uno de los tres métodos que ofrece la clase Hub para controlar el ciclo de vida de conexión y saber cuándo un usuario se ha conectado, desconectado o reconectado tras una pérdida temporal de la conexión, que son los siguientes:

  • OnConnected()

Se llamará cuando un cliente al Hub. Tras ejecutar el código que pongamos en este método, se llamará al callback start().done en el cliente.

  •  OnDisconnected()

Se llamará automáticamente cuando un cliente se desconecte del Hub.

  •  OnReconnected()

Se llamará cuando tras un periodo de desconexión, el cliente se ha podido reconectar al Hub (como por ejemplo una caída corta de la conexión a Internet).

 

Nosotros hemos implementado el método onDisconnected(), para actualizar la lista de usuarios que están conectados al chat cuando alguien se desconecte. Quizás te preguntes por qué no hemos implementado OnConnected() en vez de usar Connect(). Es simplemente por la razón de que queremos almacenar el nombre de usuario de la persona que se conecte al Hub, cosa que no podríamos hacer con el método onConnected().

Si echamos un vistazo al método Connect():

SignalR007

Lo que hacemos es almacenar el nomre de usuario de la persona que se ha conectado, el identificador de su conexión (que lo obtenemos mediante Context.ConnectionId), y posteriormente hacemos una llamada a un método del cliente.

Para realizar llamadas a los métodos de los clientes conectados a este hub, lo haremos el objeto Clients. A partir de aquí, podremos realizar acciones a todos los conectados (mediante Clients.All), al que ha realizado la petición al servidor (mediante Clients.Caller), a todos excepto el que ha hecho la petición (mediante Clients.Others) … hay muchas combinaciones existentes. De hecho, podemos hasta crear grupos determinados de usuarios para llamar sólo a un número determinado. Podemos ver todas las formas de llamar a clientes en este enlace.

Tras elegir a quién queremos llamar, escribiremos qué método queremos llamar. Para ello, simplemente escribiremos el nombre del método y pasamos los parámetros necesarios. En nuestro caso, el método que definiremos en el cliente es updateUsers y recibirá la lista de usuarios conectados y el número actual. IntelliSense no nos dará sugerencias, ya que no conoce qué métodos se han definido en el cliente (puesto que estos estarán definido en Javascript).

Cuando tenemos definidos los métodos del servidor, es hora de pasar al cliente. Nuestro código es el siguiente:

SignalR008

 

Hemos marcado aquellos métodos que tienen que ver con SignalR.

En primer lugar, debemos comentar que SignalR genera un proxy automáticamente. Esto nos permite simplificar el código que tenemos que implementar, pero podríamos utilizar igualmente SignalR sin proxy. Nosotros optamos por hacerlo ya que la sintaxis es mucho más clara.

Lo que hacemos en primer lugar es obtener la referencia al Hub, mediante $.connection.chatHub. Cabe destacar que el nombre del Hub debe ser el mismo que hemos creado en el lado del servidor, salvo que utilizamos lowerCamelCase para el nombre.

A continuación, definimos los métodos que tendrá el cliente y que podrán ser llamados desde el servidor. Para ello, se añaden mediante ref.client.nombreMetodo, donde ref es la referencia al hub anteriormente obtenida. En nuestro caso hemos definido dos métodos, updateUsers y broadcastMessage, que se encargará de mostrar en pantalla el mensaje que nos mande el servidor.

Por último, para iniciar la conexión con el Hub, lo hacemos mediante $.connection.hub.start() . Además de ello, podemos especificar una función callback para que cuando se realice la conexión, se ejecute. Se hace mediante done, y en nuestro caso lo que hacemos cuando se establezca la conexión correctamente es llamar al método Connect() del servidor para que este almacene nuestro nombre de usuario. Como podéis ver, las llamadas a métodos del servidor se hacen mediante ref.server.metodo, con el nombre del método en lowerCamelCase (fijaos que pone send y no Send).

Para que SignalR funcione correctamente tendremos que importar los scripts correspondientes en el fichero HTML, que son los que se indican en la imagen (¡y en dicho orden!).

SignalR009

El script “signalr/hubs” hace referencia al proxy se genera automáticamente y no existe físicamente, pero debemos incluirlo si queremos utilizar la sintaxis que comentamos anteriormente.

Además de estos ficheros incluiremos los scripts necesarios para ejecutar nuestro programa.

Tras ello, si ejecutamos la aplicación:

SignalR010

Aparecerá nuestro chat con el nombre de usuario que nos ha pedido. Si ahora probamos a abrir otra ventana del navegador y abrimos la aplicación, al conectarnos veremos cómo se actualiza la lista de usuarios conectados de ambos navegadores automáticamente.

SignalR011

¡Y podremos empezar a chatear!

SignalR012

 

¡Como verás, simplemente tenemos que definir métodos en cliente, en servidor y prácticamente el resto es casi magia!

Si quieres puedes descargarte el código de la aplicación desde https://github.com/Daesgar/SignalR-Chat y probarla en tu local.

¿Qué tal si le añades al programa la posibilidad de chatear con un determinado usuario de manera privada?

Daniel Escribano García

Technical Evangelist Intern

@daesgar90