Messaggistica ad alta frequenza in tempo reale con SignalR 1.x
Avviso
Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.
Questa esercitazione illustra come creare un'applicazione Web che usa ASP.NET SignalR per offrire funzionalità di messaggistica ad alta frequenza. La messaggistica ad alta frequenza in questo caso significa che gli aggiornamenti inviati a una velocità fissa; nel caso di questa applicazione, fino a 10 messaggi al secondo.
L'applicazione creata in questa esercitazione visualizza una forma che gli utenti possono trascinare. La posizione della forma in tutti gli altri browser connessi verrà quindi aggiornata per corrispondere alla posizione della forma trascinata usando gli aggiornamenti temporali.
I concetti introdotti in questa esercitazione includono applicazioni in giochi in tempo reale e altre applicazioni di simulazione.
I commenti sull'esercitazione sono benvenuti. Se si hanno domande che non sono direttamente correlate all'esercitazione, è possibile pubblicarli nel forum ASP.NET SignalR o StackOverflow.com.
Panoramica
Questa esercitazione illustra come creare un'applicazione che condivide lo stato di un oggetto con altri browser in tempo reale. L'applicazione creata è denominata MoveShape. La pagina MoveShape visualizzerà un elemento Div HTML che l'utente può trascinare; quando l'utente trascina div, la nuova posizione verrà inviata al server, che indicherà quindi a tutti gli altri client connessi di aggiornare la posizione della forma in modo che corrisponda.
L'applicazione creata in questa esercitazione si basa su una demo di Damian Edwards. Un video contenente questa demo può essere visto qui.
L'esercitazione inizierà illustrando come inviare messaggi SignalR da ogni evento che viene generato quando la forma viene trascinata. Ogni client connesso aggiornerà quindi la posizione della versione locale della forma ogni volta che viene ricevuto un messaggio.
Anche se l'applicazione funzionerà usando questo metodo, questo non è un modello di programmazione consigliato, poiché non ci sarebbe alcun limite superiore al numero di messaggi inviati, in modo che i client e il server potrebbero essere sovraccaricati con messaggi e prestazioni degraderebbero. L'animazione visualizzata nel client verrà inoltre disgiunte, poiché la forma verrà spostata immediatamente da ogni metodo anziché spostarsi senza problemi in ogni nuova posizione. Le sezioni successive dell'esercitazione illustrano come creare una funzione timer che limita la frequenza massima in cui i messaggi vengono inviati dal client o dal server e come spostare la forma in modo uniforme tra posizioni. La versione finale dell'applicazione creata in questa esercitazione può essere scaricata da Code Gallery.
Questa esercitazione contiene le sezioni seguenti:
- Prerequisiti
- Creare il progetto
- Aggiungere i pacchetti NuGet di ASP.NET SignalR e JQuery.UI
- Creare l'applicazione di base
- Aggiungere il ciclo client
- Aggiungere il ciclo del server
- Aggiungere un'animazione uniforme sul client
- Ulteriori passaggi
Prerequisiti
Questa esercitazione richiede Visual Studio 2012 o Visual Studio 2010. Se viene usato Visual Studio 2010, il progetto userà .NET Framework 4 anziché .NET Framework 4.5.
Se si usa Visual Studio 2012, è consigliabile installare l'aggiornamento ASP.NET and Web Tools 2012.2. Questo aggiornamento contiene nuove funzionalità, ad esempio miglioramenti alla pubblicazione, alla nuova funzionalità e ai nuovi modelli.
Se visual Studio 2010 è installato, assicurarsi che NuGet sia installato.
Creare il progetto
In questa sezione verrà creato il progetto in Visual Studio.
Dal menu File fare clic su Nuovo progetto.
Nella finestra di dialogo Nuovo progetto espandere C# in Modelli e selezionare Web.
Selezionare il modello di applicazione Web vuoto ASP.NET , assegnare al progetto MoveShapeDemo il nome e fare clic su OK.
Aggiungere i pacchetti NuGet SignalR e JQuery.UI
È possibile aggiungere funzionalità SignalR a un progetto installando un pacchetto NuGet. Questa esercitazione userà anche il pacchetto JQuery.UI per consentire il trascinamento e l'animazione della forma.
Fare clic su Strumenti | Gestione pacchetti NuGet | Console di Gestione pacchetti.
Immettere il comando seguente nella gestione pacchetti.
Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
Il pacchetto SignalR installa un numero di altri pacchetti NuGet come dipendenze. Al termine dell'installazione, tutti i componenti server e client necessari per l'uso di SignalR in un'applicazione ASP.NET.
Immettere il comando seguente nella console di Gestione pacchetti per installare i pacchetti JQuery e JQuery.UI.
Install-Package jQuery.ui.combined
Creare l'applicazione di base
In questa sezione verrà creata un'applicazione browser che invia la posizione della forma al server durante ogni evento di spostamento del mouse. Il server trasmette quindi queste informazioni a tutti gli altri client connessi quando viene ricevuto. Questa applicazione verrà espansa nelle sezioni successive.
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi, Classe.... Assegnare un nome alla classe MoveShapeHub e fare clic su Aggiungi.
Sostituire il codice nella nuova classe MoveShapeHub con il codice seguente.
using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; 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; } } }
La
MoveShapeHub
classe precedente è un'implementazione di un hub SignalR. Come nell'esercitazione Introduzione con SignalR, l'hub ha un metodo che i client chiameranno direttamente. In questo caso, il client invierà un oggetto contenente le nuove coordinate X e Y della forma al server, che viene quindi trasmesso a tutti gli altri client connessi. SignalR serializzerà automaticamente questo oggetto usando JSON.L'oggetto che verrà inviato al client (
ShapeModel
) contiene membri per archiviare la posizione della forma. La versione dell'oggetto nel server contiene anche un membro per tenere traccia dei dati del client archiviati, in modo che un determinato client non venga inviato i propri dati. Questo membro usa l'attributo per impedire la serializzazione e l'invioJsonIgnore
al client.Verrà quindi configurato l'hub all'avvio dell'applicazione. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto, quindi scegliere Aggiungi | Classe applicazione globale. Accettare il nome predefinito di Global e fare clic su OK.
Aggiungere l'istruzione seguente
using
dopo le istruzioni using fornite nella classe Global.asax.cs.using System.Web.Routing;
Aggiungere la riga di codice seguente nel
Application_Start
metodo della classe Global per registrare la route predefinita per SignalR.RouteTable.Routes.MapHubs();
Il file global.asax dovrebbe essere simile al seguente:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Web.Routing; namespace MoveShapeDemo { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { RouteTable.Routes.MapHubs(); } } }
Verrà quindi aggiunto il client. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto, quindi scegliere Aggiungi | Nuovo elemento. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Pagina HTML. Assegnare alla pagina un nome appropriato ( ad esempio Default.html) e fare clic su Aggiungi.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla pagina appena creata e scegliere Imposta come pagina iniziale.
Sostituire il codice predefinito nella pagina HTML con il frammento di codice seguente.
Nota
Verificare che i riferimenti allo script seguenti corrispondano ai pacchetti aggiunti al progetto nella cartella Script. In Visual Studio 2010 la versione di JQuery e SignalR aggiunta al progetto potrebbe non corrispondere ai numeri di versione seguenti.
<!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.6.4.js"></script> <script src="Scripts/jquery-ui-1.10.2.js"></script> <script src="Scripts/jquery.signalR-1.0.1.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>
Il codice HTML e JavaScript precedente crea un div rosso denominato Shape, consente il comportamento di trascinamento della forma usando la libreria jQuery e usa l'evento
drag
della forma per inviare la posizione della forma al server.Avviare l'applicazione premendo F5. Copiare l'URL della pagina e incollarlo in una seconda finestra del browser. Trascinare la forma in una delle finestre del browser; la forma nell'altra finestra del browser deve spostarsi.
Aggiungere il ciclo client
Poiché l'invio della posizione della forma in ogni evento di spostamento del mouse creerà una quantità non necessaria di traffico di rete, i messaggi dal client devono essere limitati. Verrà usata la funzione javascript setInterval
per configurare un ciclo che invia nuove informazioni sulla posizione al server a una frequenza fissa. Questo ciclo è una rappresentazione molto di base di un "ciclo di gioco", una funzione denominata ripetutamente che determina tutte le funzionalità di un gioco o di altre simulazioni.
Aggiornare il codice client nella pagina HTML per corrispondere al frammento di codice seguente.
<!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.6.4.js"></script> <script src="Scripts/jquery-ui-1.10.2.js"></script> <script src="Scripts/jquery.signalR-1.0.1.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>
L'aggiornamento precedente aggiunge la
updateServerModel
funzione, che viene chiamata su una frequenza fissa. Questa funzione invia i dati di posizione al server ogni volta che ilmoved
flag indica che sono presenti nuovi dati di posizione da inviare.Avviare l'applicazione premendo F5. Copiare l'URL della pagina e incollarlo in una seconda finestra del browser. Trascinare la forma in una delle finestre del browser; la forma nell'altra finestra del browser deve essere spostata. Poiché il numero di messaggi inviati al server verrà limitato, l'animazione non verrà visualizzata uniforme come nella sezione precedente.
Aggiungere il ciclo del server
Nell'applicazione corrente, i messaggi inviati dal server al client vengono disattivati con la frequenza con cui vengono ricevuti. Questo presenta un problema simile come è stato visto sul client; i messaggi possono essere inviati più spesso di quanto siano necessari e la connessione potrebbe diventare inondata di conseguenza. Questa sezione descrive come aggiornare il server per implementare un timer che limita la frequenza dei messaggi in uscita.
Sostituire il contenuto di con il frammento di
MoveShapeHub.cs
codice seguente.using System; using System.Collections.Generic; using System.Linq; using System.Web; 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; } } }
Il codice precedente espande il client per aggiungere la
Broadcaster
classe , che limita i messaggi in uscita usando laTimer
classe da .NET Framework.Poiché l'hub stesso è transitorio (viene creato ogni volta che è necessario), verrà
Broadcaster
creato come singleton. L'inizializzazione differita (introdotta in .NET 4) viene usata per rinviare la creazione fino a quando non è necessaria, assicurandosi che la prima istanza dell'hub venga creata completamente prima dell'avvio del timer.La chiamata alla funzione dei
UpdateShape
client viene quindi spostata all'esterno del metodo dell'hubUpdateModel
, in modo che non venga più chiamata immediatamente ogni volta che vengono ricevuti i messaggi in arrivo. Al contrario, i messaggi ai client verranno inviati a una velocità di 25 chiamate al secondo, gestite dal_broadcastLoop
timer dall'interno dellaBroadcaster
classe .Infine, invece di chiamare direttamente il metodo client dall'hub, la
Broadcaster
classe deve ottenere un riferimento all'hub operativo corrente (_hubContext
) usando .GlobalHost
Avviare l'applicazione premendo F5. Copiare l'URL della pagina e incollarlo in una seconda finestra del browser. Trascinare la forma in una delle finestre del browser; la forma nell'altra finestra del browser deve essere spostata. Non ci sarà una differenza visibile nel browser rispetto alla sezione precedente, ma il numero di messaggi inviati al client verrà limitato.
Aggiungere un'animazione fluida nel client
L'applicazione è quasi completa, ma è possibile apportare un ulteriore miglioramento, nel movimento della forma sul client mentre viene spostata in risposta ai messaggi del server. Invece di impostare la posizione della forma sulla nuova posizione specificata dal server, si userà la funzione della libreria dell'interfaccia utente di animate
JQuery per spostare la forma in modo uniforme tra la posizione corrente e quella nuova.
Aggiornare il metodo del
updateShape
client in modo che sia simile al codice evidenziato seguente:<!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.6.4.js"></script> <script src="Scripts/jquery-ui-1.10.2.js"></script> <script src="Scripts/jquery.signalR-1.0.1.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>
Il codice precedente sposta la forma dalla posizione precedente a quella nuova specificata dal server nel corso dell'intervallo di animazione (in questo caso, 100 millisecondi). Qualsiasi animazione precedente in esecuzione sulla forma viene cancellata prima dell'avvio della nuova animazione.
Avviare l'applicazione premendo F5. Copiare l'URL della pagina e incollarlo in una seconda finestra del browser. Trascinare la forma in una delle finestre del browser; la forma nell'altra finestra del browser deve essere spostata. Lo spostamento della forma nell'altra finestra dovrebbe apparire meno scalante perché il suo movimento viene interpolato nel tempo anziché essere impostato una volta per ogni messaggio in arrivo.
Ulteriori passaggi
In questa esercitazione si è appreso come programmare un'applicazione SignalR che invia messaggi ad alta frequenza tra client e server. Questo paradigma di comunicazione è utile per lo sviluppo di giochi online e altre simulazioni, ad esempio il gioco ShootR creato con SignalR.
L'applicazione completa creata in questa esercitazione può essere scaricata da Code Gallery.
Per altre informazioni sui concetti di sviluppo di SignalR, visitare i siti seguenti per il codice sorgente e le risorse di SignalR: