Tutorial: Creación de una aplicación en tiempo real de alta frecuencia con SignalR 2
En este tutorial se muestra cómo crear una aplicación web que usa ASP.NET SignalR 2 para proporcionar funcionalidad de mensajería de alta frecuencia. En este caso, "mensajería de alta frecuencia" significa que el servidor envía actualizaciones a una velocidad fija. Envía hasta 10 mensajes por segundo.
La aplicación que crea muestra una forma que los usuarios pueden arrastrar. El servidor actualiza la posición de la forma en todos los exploradores conectados para que coincidan con la posición de la forma arrastrada mediante actualizaciones con tiempo.
Los conceptos introducidos en este tutorial tienen aplicaciones en juegos en tiempo real y otras aplicaciones de simulación.
En este tutorial, hizo lo siguiente:
- Configuración del proyecto
- Creación de la aplicación base
- Asignación al centro cuando se inicia la aplicación
- Adición del cliente
- Ejecución de la aplicación
- Adición del bucle de cliente
- Adición del bucle de servidor
- Adición de animación suave
Advertencia
Esta documentación no es para la última versión de SignalR. Eche un vistazo a SignalR de ASP.NET Core.
Requisitos previos
- Visual Studio 2017 con la carga de trabajo de ASP.NET y desarrollo web.
Configuración del proyecto
En esta sección, creará el proyecto en Visual Studio 2017.
En esta sección se muestra cómo usar Visual Studio 2017 para crear una aplicación web de ASP.NET vacía y agregar las bibliotecas SignalR y jQuery.UI.
En Visual Studio, cree una aplicación web de ASP.NET.
En la ventana Nueva aplicación web ASP.NET - MoveShapeDemo, deje Vacío seleccionado y seleccione Aceptar.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>nuevo elemento.
En Agregar nuevo elemento - MoveShapeDemo, seleccione Instalado>Visual C#>Web>SignalR y, a continuación, seleccione Clase de concentrador de SignalR (v2).
Asigne a la clase el nombre MoveShapeHub y agréguela al proyecto.
Este paso crea el archivo de clase MoveShapeHub.cs. Simultáneamente, agrega un conjunto de archivos de script y referencias de ensamblado que admiten SignalR al proyecto.
Seleccione Herramientas>Administrador de paquetes NuGet>Consola del Administrador de paquetes.
En la consola del Administrador de paquetes, ejecute este comando:
Install-Package jQuery.UI.Combined
El comando instala la biblioteca jQuery UI. Se usa para animar la forma.
En Explorador de soluciones expanda el nodo Scripts.
Las bibliotecas de scripts para jQuery, jQueryUI y SignalR están visibles en el proyecto.
Creación de la aplicación base
En esta sección, creará una aplicación de explorador. La aplicación envía la ubicación de la forma al servidor durante cada evento de movimiento del mouse. El servidor difunde esta información a todos los demás clientes conectados en tiempo real. Podrá obtener más información sobre esta aplicación en secciones posteriores.
Abra el archivo MoveShapeHub.cs.
Reemplace el código del archivo MoveShapeHub.cs por este código:
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class MoveShapeHub : Hub { public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } }
Guarde el archivo.
La clase MoveShapeHub
es una implementación de un centro de SignalR. Como en el tutorial Introducción a SignalR, el centro tiene un método al que los clientes llaman directamente. En este caso, el cliente envía un objeto con las nuevas coordenadas X e Y de la forma al servidor. Esas coordenadas se transmiten a todos los demás clientes conectados. SignalR serializa automáticamente este objeto mediante JSON.
La aplicación envía el objeto ShapeModel
al cliente. Tiene miembros para almacenar la posición de la forma. La versión del objeto en el servidor también tiene un miembro para realizar un seguimiento de los datos del cliente que se almacenan. Este objeto impide que el servidor devuelva los datos de un cliente a sí mismo. Este miembro usa el atributo JsonIgnore
para evitar que la aplicación serialice los datos y los devuelva al cliente.
Asignación al centro cuando se inicia la aplicación
A continuación, configurará la asignación al centro cuando se inicie la aplicación. En SignalR 2, al agregar una clase de inicio de OWIN se crea la asignación.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>nuevo elemento.
En Agregar nuevo elemento - MoveShapeDemo seleccione Instalado>Visual C#>Web y, a continuación, seleccione Clase de startup de OWIN.
Asigne a la clase el nombre Startup y seleccione Aceptar.
Sustituya el código predeterminado del archivo Startup.cs por este código:
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))] namespace MoveShapeDemo { public class Startup { public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here app.MapSignalR(); } } }
La clase de inicio de OWIN llama a MapSignalR
cuando la aplicación ejecuta el método Configuration
. La aplicación agrega la clase al proceso de inicio de OWIN mediante el atributo de ensamblado OwinStartup
.
Adición del cliente
Agregue la página HTML para el cliente.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>página HTML.
Asigne a la página el nombre Default y seleccione Aceptar.
En el Explorador de soluciones, haga clic con el botón derecho en Default.html y seleccione Establecer como página de inicio.
Reemplace el código predeterminado del archivo Default.html por este código:
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), shapeModel = { left: 0, top: 0 }; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moveShapeHub.server.updateModel(shapeModel); } }); }); }); </script> <div id="shape" /> </body> </html>
En el Explorador de soluciones, expanda Scripts.
Las bibliotecas de scripts para jQuery y SignalR están visibles en el proyecto.
Importante
El administrador de paquetes instala una versión más reciente de los scripts de SignalR.
Actualice las referencias de script en el bloque de código para que se correspondan con las versiones de los archivos de script del proyecto.
Este código HTML y JavaScript crea un div
rojo denominado shape
. Habilita el comportamiento de arrastre de la forma mediante la biblioteca jQuery y usa el evento drag
para enviar la posición de la forma al servidor.
Ejecución de la aplicación
Puede ejecutar la aplicación para ver cómo funciona. Al arrastrar la forma alrededor de una ventana del explorador, la forma también se mueve en los demás exploradores.
En la barra de herramientas, active Depuración de scripts y, a continuación, seleccione el botón de reproducción para ejecutar la aplicación en modo de depuración.
Se abre una ventana del explorador con la forma roja en la esquina superior derecha.
Copie la dirección URL de la página.
Abra otro explorador y pegue la dirección URL en la barra de direcciones.
Arrastre la forma en una de las ventanas del explorador. La forma de la otra ventana del explorador sigue.
Aunque las funciones de aplicación usan este método, no es un modelo de programación recomendado. No hay ningún límite superior para el número de mensajes que se envían. Como resultado, los clientes y el servidor se sobrecargan con mensajes y el rendimiento se degrada. Además, la aplicación muestra una animación incorrecta en el cliente. Esta animación incorrecta se produce porque la forma se mueve al instante según cada método. Es mejor si la forma se mueve suavemente a cada nueva ubicación. A continuación, aprenderá a corregir esos problemas.
Adición del bucle de cliente
El envío de la ubicación de la forma en cada evento de movimiento del mouse crea una cantidad innecesaria de tráfico de red. La aplicación debe limitar los mensajes del cliente.
Use la función de javascript setInterval
para configurar un bucle que envíe nueva información de posición al servidor a una velocidad fija. Este bucle es una representación básica de un "bucle de juego". Es una función llamada repetidamente que impulsa toda la funcionalidad de un juego.
Reemplace el código de cliente en el archivo Default.html por este código:
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0, top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); // Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script> <div id="shape" /> </body> </html>
Importante
Debe reemplazar las referencias de script de nuevo. Deben coincidir con las versiones de los scripts del proyecto.
Este nuevo código agrega la función
updateServerModel
. Se llama en una frecuencia fija. La función envía los datos de posición al servidor cada vez que la marcamoved
indica que hay nuevos datos de posición que se van a enviar.Selección de botón Reproducir para iniciar la aplicación
Copie la dirección URL de la página.
Abra otro explorador y pegue la dirección URL en la barra de direcciones.
Arrastre la forma en una de las ventanas del explorador. La forma de la otra ventana del explorador sigue.
Dado que la aplicación limita el número de mensajes que se envían al servidor, la animación no aparecerá tan bien como al principio.
Adición del bucle de servidor
En la aplicación actual, los mensajes enviados desde el servidor al cliente salen con tanta frecuencia como se reciben. Este tráfico de red presenta un problema similar al que vemos en el cliente.
La aplicación puede enviar mensajes con más frecuencia de lo necesario. Como resultado, la conexión puede verse sobrecargada. En esta sección se describe cómo actualizar el servidor para agregar un temporizador que limita la velocidad de los mensajes salientes.
Reemplace el contenido de
MoveShapeHub.cs
por el código siguiente:using System; using System.Threading; using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class Broadcaster { private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster()); // We're going to broadcast to all clients a maximum of 25 times per second private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40); private readonly IHubContext _hubContext; private Timer _broadcastLoop; private ShapeModel _model; private bool _modelUpdated; public Broadcaster() { // Save our hub context so we can easily use it // to send to its connected clients _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>(); _model = new ShapeModel(); _modelUpdated = false; // Start the broadcast loop _broadcastLoop = new Timer( BroadcastShape, null, BroadcastInterval, BroadcastInterval); } public void BroadcastShape(object state) { // No need to send anything if our model hasn't changed if (_modelUpdated) { // This is how we can access the Clients property // in a static hub method or outside of the hub entirely _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model); _modelUpdated = false; } } public void UpdateShape(ShapeModel clientModel) { _model = clientModel; _modelUpdated = true; } public static Broadcaster Instance { get { return _instance.Value; } } } public class MoveShapeHub : Hub { // Is set via the constructor on each creation private Broadcaster _broadcaster; public MoveShapeHub() : this(Broadcaster.Instance) { } public MoveShapeHub(Broadcaster broadcaster) { _broadcaster = broadcaster; } public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster _broadcaster.UpdateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } }
Seleccione el botón Reproducir para iniciar la aplicación.
Copie la dirección URL de la página.
Abra otro explorador y pegue la dirección URL en la barra de direcciones.
Arrastre la forma en una de las ventanas del explorador.
Este código expande el cliente para agregar la clase Broadcaster
. La nueva clase limita los mensajes salientes mediante la clase Timer
de .NET Framework.
Es bueno saber que el propio centro es transitorio. Se crea cada vez que es necesario. Por lo tanto, la aplicación crea Broadcaster
como singleton. Usa la inicialización diferida para aplazar la creación de Broadcaster
hasta que sea necesario. Esto garantiza que la aplicación crea la primera instancia del centro completamente antes de iniciar el temporizador.
A continuación, la llamada a la función UpdateShape
de los clientes se mueve fuera del método del centro UpdateModel
. Ya no se llama inmediatamente cuando la aplicación recibe mensajes entrantes. En su lugar, la aplicación envía los mensajes a los clientes a una velocidad de 25 llamadas por segundo. El temporizador administra el proceso _broadcastLoop
desde dentro de la clase Broadcaster
.
Por último, en lugar de llamar al método de cliente desde el centro directamente, la clase Broadcaster
debe obtener una referencia al centro operativo actualmente _hubContext
. Obtiene la referencia con GlobalHost
.
Adición de animación suave
La aplicación está casi terminada, pero podríamos mejorarla un poco más. La aplicación mueve la forma en el cliente en respuesta a los mensajes del servidor. En lugar de establecer la posición de la forma en la nueva ubicación dada por el servidor, use la función animate
de la biblioteca JQuery UI. Puede mover la forma sin problemas entre su posición actual y nueva.
Actualice el método del cliente
updateShape
en el archivo Default.html para que tenga un aspecto similar al código resaltado:<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0, top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; // Gradually move the shape towards the new location (interpolate) // The updateRate is used as the duration because by the time // we get to the next location we want to be at the "last" location // We also clear the animation queue so that we start a new // animation and don't lag behind. $shape.animate(shapeModel, { duration: updateRate, queue: false }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); // Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script> <div id="shape" /> </body> </html>
Seleccione el botón Reproducir para iniciar la aplicación.
Copie la dirección URL de la página.
Abra otro explorador y pegue la dirección URL en la barra de direcciones.
Arrastre la forma en una de las ventanas del explorador.
El movimiento de la forma en la otra ventana aparece más correcta. La aplicación interpola su movimiento con el tiempo, en lugar de establecerse una vez por mensaje entrante.
Este código mueve la forma de la ubicación antigua a la nueva. El servidor proporciona la posición de la forma durante el intervalo de animación. En este caso, es de 100 milisegundos. La aplicación borra cualquier animación anterior que se ejecute en la forma antes de que se inicie la nueva animación.
Obtención del código
Descargar el proyecto completado
Recursos adicionales
Para más información sobre SignalR, vea los siguientes recursos:
Pasos siguientes
En este tutorial ha:
- Configuración del proyecto
- Se ha creado la aplicación base
- Se ha asignado al centro cuando se inicia la aplicación
- Se ha agregado el cliente
- Se ha ejecutado la aplicación
- Se ha agregado el bucle de cliente
- Se ha agregado el bucle de servidor
- Se ha agregado la animación suave
Vaya al siguiente artículo para aprender a crear una aplicación web que use ASP.NET SignalR 2 para proporcionar funcionalidad de difusión de servidores.