Share via


Messaggistica ad alta frequenza in tempo reale con SignalR 1.x

di Patrick Fletcher

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.

Screenshot che mostra la pagina dell'applicazione MoveShape.

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

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.

  1. Dal menu File fare clic su Nuovo progetto.

  2. Nella finestra di dialogo Nuovo progetto espandere C# in Modelli e selezionare Web.

  3. Selezionare il modello di applicazione Web vuoto ASP.NET , assegnare al progetto MoveShapeDemo il nome e fare clic su OK.

    Creazione del nuovo progetto

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.

  1. Fare clic su Strumenti | Gestione pacchetti NuGet | Console di Gestione pacchetti.

  2. 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.

  3. 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.

  1. 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.

  2. 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'invio JsonIgnore al client.

  3. 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.

    Aggiungi classe applicazione globale

  4. Aggiungere l'istruzione seguente using dopo le istruzioni using fornite nella classe Global.asax.cs.

    using System.Web.Routing;
    
  5. 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();
            }
        }
    }
    
  6. 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.

  7. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla pagina appena creata e scegliere Imposta come pagina iniziale.

  8. 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.

  9. 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.

    Screenshot che mostra la modalità di trascinamento di una forma in una finestra del browser in un'altra finestra.

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.

  1. 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 il moved flag indica che sono presenti nuovi dati di posizione da inviare.

  2. 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.

    Screenshot che mostra come una forma trascinata in una finestra del browser viene spostata in un'altra finestra quando si aggiunge un ciclo client.

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.

  1. 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 la Timer 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'hub UpdateModel , 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 della Broadcaster 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

  2. 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.

    Screenshot che mostra come una forma trascinata in una finestra del browser viene spostata in un'altra finestra quando si aggiunge un ciclo server.

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.

  1. 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.

  2. 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.

    Screenshot che mostra come una forma trascinata in una finestra del browser viene spostata in un'altra finestra quando si aggiunge un'animazione fluida nel client.

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: