Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 17 de enero de 2012
Uau!!Una aplicación que mezcla internet, asincronía, y múltiples usuarios colaborando e interactuando al mismo tiempo siempre es merecedora de un “¡uau!”. Seguro que, al igual que un servidor, en algún momento os habéis quedado maravillados con la interactividad que presentan algunos sistemas web modernos, como Facebook, Google Docs, o muchos otros, en las que estamos recibiendo actualizaciones, prácticamente en tiempo real, sin necesidad de recargar la página.

Por ejemplo, en Google Docs, si estamos editando un documento online y otro usuario accede al mismo, podemos ver sobre la marcha que ha entrado, e incluso las modificaciones que va realizando sobre el documento. O algo más cotidiano, en un simple chat vía web van apareciendo los mensajes tecleados por nuestros compañeros de sala como por arte de magia. Ambos sistemas utilizan el mismo tipo de solución: el envío asíncrono de datos entre servidor y clientes en tiempo real.

En esta serie de artículos veremos cómo podemos implementar sorprendentes funcionalidades de este tipo utilizando SignalR, un framework open source desarrollado por gente del equipo de ASP.NET, que nos facilitará bastante la tarea.

¿Cómo puede el servidor enviar eventos al cliente de forma asíncrona?

Crear este tipo de sistemas usando herramientas convencionales nos puede causar algunos dolores de cabeza, principalmente porque los protocolos que sustentan la web están basados en un modelo cliente-servidor síncrono: uno o varios clientes realizan una conexión hacia el servidor y le transmiten una acción a realizar, éste la procesa y les retorna la respuesta, cerrándose la conexión de forma inmediata.

PollingA priori, no hay forma de que el servidor sea el que notifique a los clientes los cambios de estado (por ejemplo, la llegada en un chat de un mensaje procedente de otro usuario), salvo que éstos utilicen un mecanismo de polling, es decir, que estén continuamente estableciendo conexiones con el servidor para ver si hay algún nuevo evento a tener en cuenta.

Aunque válido en determinados escenarios, hay otros en los que se trata de una solución demasiado costosa, sobre todo cuando hay que gestionar un alto número de clientes conectados.

El ideal sería utilizar una conexión persistente, siempre abierta, entre cliente y servidor, que permitiría el envío y recepción de mensajes y eventos de forma bidireccional entre ambos. De esta forma, si el servidor tiene algo que enviar a sus clientes, simplemente tendría que transmitirlo por el canal que mantendría abierto con cada uno de ellos.

Conexión persistenteSin embargo, hasta ahora esto sólo se podía conseguir usando sockets, lo cual, en entorno web, requería la existencia de algún tipo de elemento activo sobre la página (Silverlight, Flash, o applets Java, por ejemplo) capaz de establecer este tipo de comunicaciones.

Afortunadamente, la W3C parece dispuesta a cambiar esta situación al introducir de forma nativa los famosos WebSockets, cuya definición se encuentra todavía en borrador. Esta nueva API permitirá abrir conexiones directas desde el navegador usando Javascript, por lo que podría ayudarnos bastante una vez su implementación sea universal en los agentes de usuario. De momento no es así, aunque ya está disponible en algunos de ellos (podéis ver una demo simple aquí con Chrome).
Logo de HTML5
También existe otra iniciativa de la W3C que podría ayudar a enviar mensajes o eventos desde el servidor a los clientes suscritos, llamada Server-Sent Events. Como en el caso anterior, se encuentra en borrador, aunque ya algunos navegadores lo implementan (podéis ver una demo aquí con Chrome), por lo que todavía no podemos utilizarla de forma segura.

Por esta razón, existen hoy en día múltiples soluciones que permiten solventar las limitaciones del protocolo, como las englobadas bajo la denominación Server push o Comet, aprovechando los recursos existentes en los protocolos utilizados para crear, o al menos simular, este canal abierto continuo entre cliente y servidor utilizando polling, long polling, HTTP streaming, y otros artificios.

Long pollingPor ejemplo, el mecanismo long polling utiliza peticiones HTTP para crear una conexión “pseudopersistente”. El servidor, en lugar de procesar la petición y retornar la respuesta de forma inmediata, espera hasta que haya disponible algún evento o mensaje a enviar al cliente; en este momento, lo retorna como respuesta a la petición original y cierra la conexión. El cliente, por su parte, procesa esta respuesta y realiza inmediatamente después una nueva petición al servidor, que volverá a quedar abierta a la espera de mensajes, y así sucesivamente.

En definitiva, se trata de un mecanismo más limpio y eficiente que el polling, puesto que evita gran cantidad de peticiones absurdas que se producen cuando en el servidor no hay eventos pendientes de notificar. Además, dado que utiliza HTTP estándar, es válida para todo tipo de agentes de usuario, y bastante amigable para proxies, filtros, firewalls y otros inconvenientes que puede haber por el camino entre los dos extremos.

Y en este punto es donde entra en escena SignalR, un conjunto de componentes desarrollados por Damian Edwards y David Fowler, miembros del equipo de ASP.NET en Microsoft, que nos abstrae de los detalles subyacentes y nos ofrece la visión y ventajas de un entorno conectado en el que podemos comunicar cliente y servidor bidireccionalmente, de forma asíncrona, y con una sencillez pasmosa. SignalR nos hace ver como si cliente y servidor estuvieran conectados de forma continua y facilita el envío de mensajes asíncronos bidireccionales entre ambos extremos.

Por último, es importante decir que SignalR no es específico para ASP.NET MVC, ni para WebForms: podemos utilizarlo con cualquier tipo de proyecto web. De hecho, incluso se puede utilizar en otro tipo de proyectos usando un servidor self-hosted :-)

SignalR, conceptualmente

SignalR ofrece una visión a muy alto nivel de la comunicación entre el servidor y los múltiples clientes que se encuentren a él conectados. Y cuando digo “alto nivel”, creedme que estoy hablando de muchos metros de altura ;-)

Como desarrolladores, trabajaremos sobre una conexión virtualmente siempre abierta: en servidor podremos detectar cuándo se ha conectado un nuevo cliente, cuándo se ha desconectado, recibir mensajes de éstos, enviar mensajes a los clientes conectados…, en definitiva, todo lo que podemos necesitar para crear aplicaciones asíncronas multiusuario.

Sin embargo, en realidad estas conexiones persistentes no existen, o no tienen por qué existir. Se trata de una abstracción creada por SignalR, que el que se encargará del trabajo sucio que hay por debajo, manteniendo la conexión de los clientes con el servidor mediante distintos mecanismos denominados “transportes”, que son el conjunto de tecnologías utilizadas para mantener crear la conexión continua, o al menos la ilusión de su existencia.

Lo interesante de los protocolos de transporte es que pueden ser sustituidos de forma transparente sin afectar a nuestras aplicaciones, que trabajarán aisladas de estos detalles. Nuestros sistemas funcionarán exactamente igual sea cual sea el transporte utilizado, lo que permite que éste sea elegido en cada escenario en función de la disponibilidad de las tecnologías en ambos extremos.

Por ejemplo, el transporte Websockets es capaz de crear una conexión con el servidor y mantenerla abierta de forma continua, aunque requiere que esta tecnología esté disponible tanto en el cliente (en el caso de clientes web, es necesario que el navegador implemente Websockets) como en el servidor.

Long polling, el transporte utilizado por defecto en SignalRDebido a ello, y para asegurar la máxima compatibilidad con los clientes, actualmente se utiliza por defecto el transporte denominado Long polling, que ya hemos comentado anteriormente.

Observad que, a pesar de la relativa complejidad que supondría implementar algo así a mano, nosotros no tendremos que hacer nada: SignalR se encarga de llevar a cabo todas estas tareas para ofrecernos la sensación de estar siempre conectados.

Su componente cliente será el encargado de realizar las conexiones, mantenerse a la espera de noticias del servidor, reconectar cuando se reciban eventos o cuando por cualquier otra causa se haya perdido la conectividad, etc., ofreciéndonos una superficie de desarrollo muy simplificada.

El lado servidor de SignalR, por otra parte, será el encargado de recibir la conexión y mantenerla en espera, almacenar los mensajes recibidos, realizar el seguimiento de clientes conectados, enviar mensajes a través de un bus interno, etc., y de la misma forma, ofreciéndonos un API bastante simple para implementar nuestros servicios.

Implementación de servicios con SignalR

SignalR nos ofrece dos fórmulas para trabajar sobre las conexiones que crea con el servidor:
  • usando “conexiones persistentes”, es la de más bajo nivel y proporciona mecanismos simples para registrar conexiones y desconexiones de clientes y comunicarse de forma bidireccional con ellos. De hecho, esta forma de crear servicios es bastante similar a como hacemos utilizando sockets.
  • usando “hubs”, que ofrece una abstracción aún mayor, permitiendo la comunicación entre cliente y servidor de forma casi mágica. Esta es la opción que convendrá utilizar en la mayoría de ocasiones, por la potencia que aporta y su gran comodidad de uso.
En cualquiera de los dos casos, y ya centrándonos en el entorno web más habitual, donde el servidor es una aplicación ASP.NET y los clientes van a ser las páginas o vistas en las que tendremos un motor de scripting, la implementación de servicios consistirá en:
  • en el servidor, crear el servicio (también llamado endpoint) con las funcionalidades que nos interese, utilizando las clases disponibles en el ensamblado SignalR.
  • en cliente, crear el consumidor del servicio utilizando las clases disponibles en la biblioteca de scripts jQuery.SignalR.js (o su correspondiente versión minimizada).
Cada una de las dos fórmulas citadas tiene sus particularidades, por lo que las estudiaremos mediante el desarrollo de ejemplos independientes en futuros posts de la serie.

Pero primero, veamos rápidamente cómo podemos incluir este componente en nuestros proyectos, aunque desde luego más sencillo no puede ser… ;-)

Instalación de SignalR

El sitio web oficial del producto (signalr.net), a día de hoy, es una simple redirección hacia Github, donde se encuentra la documentación y el código fuente del proyecto. Aunque podríamos descargarlo desde ahí, la opción más sencilla, como siempre, es utilizar Nuget:

PM> Install-Package signalr
Attempting to resolve dependency 'SignalR.Server (≥ 0.3.5)'.
Attempting to resolve dependency 'Microsoft.Web.Infrastructure (≥ 1.0.0.0)'.
Attempting to resolve dependency 'SignalR.Js (≥ 0.3.5)'.
Attempting to resolve dependency 'jQuery (≥ 1.6)'.
Successfully installed 'Microsoft.Web.Infrastructure 1.0.0.0'.
Successfully installed 'SignalR.Server 0.3.5'.
Successfully installed 'SignalR.Js 0.3.5'.
Successfully installed 'SignalR 0.3.5'.
Successfully added 'Microsoft.Web.Infrastructure 1.0.0.0' to SignalRDemo.
Successfully added 'SignalR.Server 0.3.5' to SignalRDemo.
Successfully added 'SignalR.Js 0.3.5' to SignalRDemo.
Successfully added 'SignalR 0.3.5' to SignalRDemo.

Esta instalación incluye, además de algún elemento infraestructural, dos componentes de SignalR:
  • SignalR.Server, que es la biblioteca de servidor principal para integrar en aplicaciones ASP.NET. 
  • SignalR.Js, la biblioteca Javascript necesaria para conectar desde cliente (páginas web) con el servidor.
Existen también otros clientes específicos para .NET, como SignalR.Client (cliente genérico), SignalR.Client.Silverlight (específico para SL), o SignalR.Client.WP7 (específico para Windows Phone 7), que podemos instalar de forma independiente.

Además, tanto en Nuget como en el sitio web del producto podéis encontrar otros paquetes interesantes a los que vale la pena echar un vistazo, como SignalR.Sample, un ejemplo completo de uso de este componente, SignalR.SelfHost, que permite activar el servidor sin usar ASP.NET, o SignalR.Websockets, un adaptador (o transporte, en argot SignalR) para usar Websockets para el mantenimiento de la conexión entre cliente y servidor.

Observaréis que en todos los casos se trata de versiones muy preliminares pero que podemos ir probando y disfrutando desde ya, porque funcionan bastante bien. Podéis comprobarlo accediendo a http://jabbr.net/, un chat implementado sobre SignalR donde podréis encontrar charlando hasta a los mismísimos padres de la criatura. :-)

En el próximo post veremos cómo implementar clientes y servicios SignalR utilizando conexiones persistentes, el enfoque de menor nivel ofrecido por este fantástico componente.

Publicado en: Variable not found.

17 Comentarios:

Sergio León dijo...

Hola Jose:

Primero, felicitarte por el artículo y por tu blog!

Hablando de SignalR, tengo unas dudas después de leer tu artículo:

El tema del long pooling ¿Cómo se lleva con HTTP? Es decir, con un protocolo sin estado como HTTP, si yo hago una petición GET y el servidor se queda pinchado hasta que tenga información para devolverme la respuesta ¿No estamos consumiendo un exceso de recursos en el servidor y agotando los Threads de ASP.NET? Seguro que hay truco pero ahora mismo no lo veo.

Por otro lado, me ha parecido entender que SignalR será capaz de elegir cual es el mejor método para implementar su funcionalidad, según el navegador. Es decir, si el navegador soporta WebSockets, SignalR irá por WebSockets, sino irá por long pooling ¿Esto lo hace SignalR sólo o hay que ayudarle a elegir y/o descargar en cliente los ficheros .js adecuados según el navegador y sus características soportadas?

Gracias y un saludo.

Sergio León dijo...

Me contesto a mi mismo para la primera pregunta y entiendo que utilizará algún mecanismo asíncrono (como la interfaz IHttpAsyncHanlder de los WebForms) ¿Van por ahí los tiros? Es que me parece que cuando se va por el camino asíncrono los Threads de ASP.NET se liberan hasta que termina la operación solicitada...

Un saludo y perdona si te lleno el blog de comentarios ;-)

josé M. Aguilar dijo...

Hola, Sergio.

Efectivamente, se utiliza internamente asincronía para bloquear lo menos posible. Eso sí, el hecho de mantener una conexión abierta consume recursos de todas formas, por lo que no tiene capacidad ilimitada.

En cualquier caso, los números que he visto por ahí de pruebas de carga de conexiones simultáneas son bastante altos y, para los escenarios más espectaculares, también están trabajando en permitir escalado hacia varios servidores para repartir la carga.

Por otra parte, la selección de transporte (polling, sockets) debería ser automática y transparente, aunque también puede forzarse desde el código cliente.

Saludos & gracias por comentar!

Kiquenet dijo...

Hola,

Por lo que veo ahora en signalr.net se instala vía comando Nuget así:

Install-Package Microsoft.AspNet.SignalR

No he visto que aparezca en la Online Gallery de Extension Manager de VS 2010.


También sería interesante poder utilizar SignalR en clientes Windows Forms, para por ejemplo, realizar notificaciones estilo de progress bar. Se podría considerar entonces que la propia aplicación Winforms es Servidor y Cliente de SignalR.

No sé si esta casuística es posible, por el namespace Microsoft.AspNet.SignalR parece que SignalR se aplica sólo a Web.

Saludos y gracias.

josé M. Aguilar dijo...

Hola!

Efectivamente, Signalr no aparece en la galería de extensiones, sino en la de Nuget. Ten en cuenta que no es ninguna extensión de VS, simplemente un framework de desarrollo :)

En cuanto a lo que comentas de Windows forms, efectivamente, hay bibliotecas específicas para self-hosting (server) y cliente en prácticamente cualquier tipo de sistemas. Puedes ver la documentación de signalr (https://github.com/SignalR/SignalR/wiki).

Saludos!

Anónimo dijo...

Hola José...he implementado signalR en un proyecto web...y funciona a la perfección, mi duda es...consume muchos recursos!?!?el proyecto que se desarrolla es algo grande, y por ahora no baja el rendimiento,..pero quisiera saber si pudiera bajar la productividad el uso de signalR!??!o consume muy poco!?!?gracias!!

josé M. Aguilar dijo...

Hola!

SignalR está muy optimizado para dar soporte a gran cantidad de usuarios ofreciendo buen rendimiento, pero obviamente éste siempre dependerá del número de conexiones y, casi más importante, del número de mensajes que circulen.

Si tu aplicación va a mantener miles de usuarios conectados y a enviar varios miles de mensajes por segundo, probablemente lo notarás ;)

De todas formas, cuando eso llegue, tienes alternativas y truquillos que puedes hacer para mejorar su rendimiento, o incluso escalar el servicio:

http://www.asp.net/signalr/overview/performance-and-scaling

Saludos.

leviatanMX dijo...

Excelente articulo...

al fin una competencia para node.js...

apenas empezaba con las 2 tecnologias, pero me facino asp.net mvc el cual es mucho mas amigable que node.js, lo que me llamaba de node.js era exactamente lo que ahora puedes hacer en asp.net con SignaIR..!

sabemos que solo se aplica para cierto tipo de proyectos, pero me parece excelente.. que hayan logrado desarrollar esto para asp.net...


Admirador dijo...

Gran blog !!.
Viendo el artículo y comentarios me parece muy potente.
Los ejemplos que he visto la mayoría es el típico chat.
Me gustaría ver algún ejemplo más real y empresarial, por ejemplo:

una aplicación Windows Forms que haga de Server y de Client

una aplicación Windows Forms que haga Client y un Servicio Windows o Aplicación de Consola que haga de Server.

Sé que muchos desarrolladores en la comunidad aportan en github o codeplex, o codeproject igual existe un ejemplo completo de estas características.
Gracias.

José María Aguilar dijo...

Hola!

Muchas gracias por los comentarios.

Y voy a aprovechar para comentar algo que me preguntan muy a menudo: por qué hay tantos ejemplos de chat en SignalR.

Es cierto que la mayoría de ejemplos son un chat, pero, desde mi punto de vista, tiene una explicación lógica: no distrae del objetivo del post.

Normalmente, el objetivo de este tipo de artículos es mostrar el funcionamiento de SignalR, y éste es el mismo para todo tipo de aplicaciones. Al usar un chat, no es necesario dar explicaciones sobre lo que se pretende construir porque todos sabemos lo que es, por lo que permite focalizar el contenido del artículo. Y la base tecnológica de un chat es exactamente la misma que la de otro tipo de sistemas realtime, por lo que así podemos cubrir el objetivo.

Incluso el uso de Signalr desde otro tipo de aplicaciones (como el ejemplo de Winforms como cliente o servidor que comentas), también podría demostrarse sobre un chat, pues el foco de un artículo de ese tipo podría ser el uso del self-hosting (necesario para hacer que ese tipo de aplicaciones puedan actuar como servidores Signalr) o el cliente .NET (necesario para que ese tipo de aplicaciones actúen como clientes SignalR).

En definitiva: si encuentras ejemplos de un chat, no hay que mirarlo como un bloque de código para usar directamente en tus aplicaciones. Hay que usar ese ejemplo simple para entender el uso y extrapolarlo para otros escenarios y aplicaciones. Dado que no es necesario entender lo que pretendemos hacer (un chat) debido a su simpleza, tendremos más fácil centrarnos en lo realmente importante, el uso de SignalR.

Disculpad la parrafada ;-)

Muchas gracias!

José María Aguilar dijo...

@Admirador, muchas gracias :)

No sé si existirá algún ejemplo completo sobre los escenarios que comentas, pero en realidad sólo tienes que buscar dos tipos de ejemplos que de forma independiente podrás encontrar bastantes:

- Self-hosting en Signalr
- cliente .NET de Signalr

Con esto ya podrás ver cómo alojar servicios SignalR en tu aplicación Winforms (o cualquier otro tipo), y para acceder a servicios SignalR desde cualquier tipo de aplicación .NET (consola, winforms, etc.)

Saludos.

Maria Andrea dijo...

Hola Jose, Buenas Tardes.

Muy bueno tu articulo, pero me gustaría hacerte una pregunta, SignalR se puede utilizar en:

El caso es:
El cliente 1 debe preguntar a uno o varios clientes si posee una información especifica, va al servidor guarda la información (Server) y enseguida se debe notificar al cliente 2, el cliente 2 contesta la pregunta y guarda la información (Server) y al dar respuesta, se le debe notificar al cliente 1, este caso se puede dar para varios clientes.

Nuestro cliente es WPF y el servidor WCF.

Muchas Gracias

José María Aguilar dijo...

Hola, María Andrea.

Por lo que comentas, sí podría ser un escenario de uso de SignalR.

Simplemente tendrías que tener un Hub en el servidor al que se conectarían tus clientes. Dicho Hub se usaría como canal para enviar y recibir mensajes en tiempo real. Es decir, la secuencia sería:

Cliente1->Hub->Cliente2
Cliente2->Hub->Cliente1

En cada cliente WPF tendrías el componente cliente Signalr.

Suerte!

Maria Andrea dijo...

Hola José.

Muchas gracias por tu valiosa ayuda, iniciare mi trabajo.

Saludos.

Admirador dijo...

Andream sería interesante ese ejemplo de proyecto real.

- Hub en Servicio WCF
- Self-hosting en Signalr
- cliente .NET de Signalr

Quizá haya un ejemplo con código C# en github con esa casuística.

Antonio Espinosa dijo...

felicidades se nota tu mvp en la calidad de tu articulo estamos pendientes de nuevos post

Anónimo dijo...

Gracias