Esercitazione: Creare un'app in tempo reale ad alta frequenza con SignalR 2

Questa esercitazione illustra come creare un'applicazione Web che usa ASP.NET SignalR 2 per fornire funzionalità di messaggistica ad alta frequenza. In questo caso, la "messaggistica ad alta frequenza" indica che il server invia gli aggiornamenti a una velocità fissa. Si inviano fino a 10 messaggi al secondo.

L'applicazione creata visualizza una forma che gli utenti possono trascinare. Il server aggiorna la posizione della forma in tutti i browser connessi in modo che corrisponda alla posizione della forma trascinata utilizzando gli aggiornamenti temporali.

I concetti introdotti in questa esercitazione includono applicazioni in giochi in tempo reale e altre applicazioni di simulazione.

In questa esercitazione:

  • Configurare il progetto
  • Creare l'applicazione di base
  • Eseguire il mapping all'hub all'avvio dell'app
  • Aggiungere il client
  • Eseguire l'app
  • Aggiungere il ciclo client
  • Aggiungere il ciclo del server
  • Aggiungi animazione fluida

Avviso

Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.

Prerequisiti

Configurare il progetto

In questa sezione viene creato il progetto in Visual Studio 2017.

Questa sezione illustra come usare Visual Studio 2017 per creare un'applicazione Web ASP.NET vuota e aggiungere le librerie SignalR e jQuery.UI.

  1. In Visual Studio creare un'applicazione Web ASP.NET.

    Creare il Web

  2. Nella finestra New ASP.NET Web Application - MoveShapeDemo (Nuova applicazione Web ASP.NET - MoveShapeDemo) lasciare vuota selezionata e selezionare OK.

  3. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo elemento.

  4. In Aggiungi nuovo elemento - MoveShapeDemo selezionare Installato>Visual C#>Web>SignalR e quindi selezionare Classe hub SignalR (v2) .

  5. Denominare la classe MoveShapeHub e aggiungerla al progetto.

    Questo passaggio crea il file di classe MoveShapeHub.cs. Contemporaneamente, aggiunge un set di file di script e riferimenti ad assembly che supportano SignalR al progetto.

  6. Fare clic su Strumenti>Gestione Pacchetti NuGet>Console di Gestione pacchetti.

  7. Nella console di Gestione pacchetti eseguire questo comando:

    Install-Package jQuery.UI.Combined
    

    Il comando installa la libreria dell'interfaccia utente di jQuery. Usarlo per animare la forma.

  8. In Esplora soluzioni espandere il nodo Script.

    Riferimenti alla libreria di script

    Le librerie di script per jQuery, jQueryUI e SignalR sono visibili nel progetto.

Creare l'applicazione di base

In questa sezione viene creata un'applicazione browser. L'app invia la posizione della forma al server durante ogni evento di spostamento del mouse. Il server trasmette queste informazioni a tutti gli altri client connessi in tempo reale. Altre informazioni su questa applicazione sono disponibili nelle sezioni successive.

  1. Aprire il file MoveShapeHub.cs .

  2. Sostituire il codice nel file MoveShapeHub.cs con questo codice:

    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; }
        }
    }
    
  3. Salvare il file.

La MoveShapeHub classe è un'implementazione di un hub SignalR. Come nell'esercitazione Introduzione a SignalR , l'hub ha un metodo che i client chiamano direttamente. In questo caso, il client invia un oggetto con le nuove coordinate X e Y della forma al server. Tali coordinate vengono trasmesse a tutti gli altri client connessi. SignalR serializza automaticamente questo oggetto usando JSON.

L'app invia l'oggetto ShapeModel al client. Dispone di membri per archiviare la posizione della forma. Anche la versione dell'oggetto nel server dispone di un membro per tenere traccia dei dati del client archiviati. Questo oggetto impedisce al server di inviare nuovamente i dati di un client a se stesso. Questo membro usa l'attributo JsonIgnore per impedire all'applicazione di serializzare i dati e inviarli al client.

Eseguire il mapping all'hub all'avvio dell'app

Successivamente, si configura il mapping all'hub all'avvio dell'applicazione. In SignalR 2 aggiungere una classe di avvio OWIN crea il mapping.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo elemento.

  2. In Aggiungi nuovo elemento - MoveShapeDemo selezionare Installato>Visual C#>Web e quindi selezionare Classe di avvio OWIN.

  3. Assegnare alla classe il nome Startup e selezionare OK.

  4. Sostituire il codice predefinito nel file Startup.cs con questo codice:

    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 classe di avvio OWIN chiama MapSignalR quando l'app esegue il Configuration metodo . L'app aggiunge la classe al processo di avvio di OWIN usando l'attributo OwinStartup assembly.

Aggiungere il client

Aggiungere la pagina HTML per il client.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>pagina HTML.

  2. Assegnare alla pagina il nome Predefinito e selezionare OK.

  3. In Esplora soluzioni fare clic con il pulsante destro del mouse su Default.html e scegliere Imposta come pagina iniziale.

  4. Sostituire il codice predefinito nel file Default.html con questo codice:

    <!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>
    
  5. In Esplora soluzioni espandere Script.

    Le librerie di script per jQuery e SignalR sono visibili nel progetto.

    Importante

    Gestione pacchetti installa una versione successiva degli script SignalR.

  6. Aggiornare i riferimenti allo script nel blocco di codice in modo che corrispondano alle versioni dei file di script nel progetto.

Questo codice HTML e JavaScript crea un rosso div denominato shape. Consente il comportamento di trascinamento della forma tramite la libreria jQuery e usa l'evento drag per inviare la posizione della forma al server.

Eseguire l'app

È possibile eseguire l'app per se'e funziona. Quando si trascina la forma intorno a una finestra del browser, anche la forma viene spostata negli altri browser.

  1. Sulla barra degli strumenti attivare Debug script e quindi selezionare il pulsante Play per eseguire l'applicazione in modalità debug.

    Screenshot dell'attivazione della modalità di debug e selezione della riproduzione.

    Viene visualizzata una finestra del browser con la forma rossa nell'angolo superiore destro.

  2. Copiare l'URL della pagina.

  3. Aprire un altro browser e incollare l'URL nella barra degli indirizzi.

  4. Trascinare la forma in una delle finestre del browser. La forma nell'altra finestra del browser segue.

Anche se le funzioni dell'applicazione che usano questo metodo, non è un modello di programmazione consigliato. Non esiste alcun limite massimo per il numero di messaggi inviati. Di conseguenza, i client e il server vengono sovraccaricati con messaggi e prestazioni ridotte. Inoltre, l'app visualizza un'animazione non contigua nel client. Questa animazione cretina si verifica perché la forma si sposta immediatamente da ogni metodo. È meglio se la forma si sposta senza problemi in ogni nuova posizione. Successivamente, si apprenderà come risolvere questi problemi.

Aggiungere il ciclo client

L'invio della posizione della forma in ogni evento di spostamento del mouse crea una quantità non necessaria di traffico di rete. L'app deve limitare i messaggi dal client.

Usare la funzione javascript setInterval per configurare un ciclo che invia nuove informazioni sulla posizione al server a una velocità fissa. Questo ciclo è una rappresentazione di base di un "ciclo di gioco". Si tratta di una funzione chiamata ripetutamente che determina tutte le funzionalità di un gioco.

  1. Sostituire il codice client nel file Default.html con questo codice:

    <!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

    È necessario sostituire nuovamente i riferimenti allo script. Devono corrispondere alle versioni degli script nel progetto.

    Questo nuovo codice aggiunge la updateServerModel funzione . Viene chiamato su una frequenza fissa. La 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. Selezionare il pulsante Riproduci per avviare l'applicazione

  3. Copiare l'URL della pagina.

  4. Aprire un altro browser e incollare l'URL nella barra degli indirizzi.

  5. Trascinare la forma in una delle finestre del browser. La forma nell'altra finestra del browser segue.

Poiché l'app limita il numero di messaggi inviati al server, l'animazione non viene visualizzata come uniforme all'inizio.

Aggiungere il ciclo del server

Nell'applicazione corrente, i messaggi inviati dal server al client escono con la frequenza con cui vengono ricevuti. Questo traffico di rete presenta un problema simile visualizzato nel client.

L'app può inviare messaggi più spesso di quanto siano necessari. La connessione può diventare inondata di conseguenza. Questa sezione descrive come aggiornare il server per aggiungere un timer che limita la frequenza dei messaggi in uscita.

  1. Sostituire il contenuto di MoveShapeHub.cs con questo codice:

    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; }
        }
        
    }
    
  2. Selezionare il pulsante play per avviare l'applicazione.

  3. Copiare l'URL della pagina.

  4. Aprire un altro browser e incollare l'URL nella barra degli indirizzi.

  5. Trascinare la forma in una delle finestre del browser.

Questo codice espande il client per aggiungere la Broadcaster classe . La nuova classe limita i messaggi in uscita usando la Timer classe di .NET Framework.

È bene imparare che l'hub stesso è transitorio. Viene creato ogni volta che è necessario. L'app crea quindi come Broadcaster singleton. Usa l'inizializzazione differita per rinviare la Broadcastercreazione fino a quando non è necessaria. Ciò garantisce che l'app crei completamente la prima istanza dell'hub prima di avviare il timer.

La chiamata alla funzione dei UpdateShape client viene quindi spostata all'esterno del metodo dell'hub UpdateModel . Non viene più chiamato immediatamente ogni volta che l'app riceve i messaggi in arrivo. L'app invia invece i messaggi ai client a una velocità di 25 chiamate al secondo. Il processo viene gestito 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 _hubContext corrente. Ottiene il riferimento con .GlobalHost

Aggiungi animazione fluida

L'applicazione è quasi terminata, ma è possibile apportare un ulteriore miglioramento. L'app sposta la forma sul client in risposta ai messaggi del server. Anziché impostare la posizione della forma sulla nuova posizione specificata dal server, usare la funzione della libreria dell'interfaccia utente di animate JQuery. Può spostare la forma senza problemi tra la posizione corrente e quella nuova.

  1. Aggiornare il metodo del updateShape client nel file Default.html in modo che sia simile al codice evidenziato:

    <!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>
    
  2. Selezionare il pulsante play per avviare l'applicazione.

  3. Copiare l'URL della pagina.

  4. Aprire un altro browser e incollare l'URL nella barra degli indirizzi.

  5. Trascinare la forma in una delle finestre del browser.

Il movimento della forma nell'altra finestra appare meno sdollento. L'app esegue l'interpolazione del movimento nel tempo anziché essere impostata una sola volta per ogni messaggio in arrivo.

Questo codice sposta la forma dalla posizione precedente a quella nuova. Il server fornisce la posizione della forma nel corso dell'intervallo di animazione. In questo caso, si tratta di 100 millisecondi. L'app cancella qualsiasi animazione precedente in esecuzione sulla forma prima dell'avvio della nuova animazione.

Ottenere il codice

Scaricare il progetto completato

Risorse aggiuntive

Per altre informazioni su SignalR, vedere le risorse seguenti:

Passaggi successivi

In questa esercitazione:

  • Configurare il progetto
  • Creazione dell'applicazione di base
  • Mappato all'hub all'avvio dell'app
  • Aggiunta del client
  • Esecuzione dell'app
  • Aggiunta del ciclo client
  • Aggiunta del ciclo del server
  • Aggiunta di un'animazione fluida

Passare all'articolo successivo per informazioni su come creare un'applicazione Web che usa ASP.NET SignalR 2 per fornire funzionalità di trasmissione server.