Esercitazione: Trasmissione server con SignalR 2

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 2 per fornire funzionalità di trasmissione server. Trasmissione server significa che il server avvia le comunicazioni inviate ai client.

L'applicazione che verrà creata in questa esercitazione simula un ticker azionario, uno scenario tipico per la funzionalità di trasmissione server. Periodicamente, il server aggiorna in modo casuale i prezzi delle azioni e trasmette gli aggiornamenti a tutti i client connessi. Nel browser i numeri e i simboli nelle colonne Cambia e % cambiano dinamicamente in risposta alle notifiche dal server. Se si aprono altri browser allo stesso URL, vengono visualizzati tutti gli stessi dati e le stesse modifiche ai dati contemporaneamente.

Screenshot che mostra in che modo più Web browser mostrano contemporaneamente gli stessi dati aggiornati.

In questa esercitazione:

  • Creare il progetto
  • Configurare il codice del server
  • Esaminare il codice del server
  • Configurare il codice client
  • Esaminare il codice client
  • Test dell'applicazione
  • Abilitazione della registrazione

Importante

Se non si vuole eseguire i passaggi della compilazione dell'applicazione, è possibile installare il pacchetto SignalR.Sample in un nuovo progetto Vuoto ASP.NET Applicazione Web. Se si installa il pacchetto NuGet senza eseguire la procedura descritta in questa esercitazione, è necessario seguire le istruzioni nel file readme.txt . Per eseguire il pacchetto è necessario aggiungere una classe di avvio OWIN che chiama il ConfigureSignalR metodo nel pacchetto installato. Se non si aggiunge la classe di avvio OWIN, verrà visualizzato un errore. Vedere la sezione Installare l'esempio StockTicker di questo articolo.

Prerequisiti

Creare il progetto

Questa sezione illustra come usare Visual Studio 2017 per creare un'applicazione Web ASP.NET vuota.

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

    Screenshot che mostra come creare un'applicazione Web ASP.NET.

  2. Nella finestra New ASP.NET Web Application - SignalR.StockTicker (Nuova applicazione Web ASP.NET - SignalR.StockTicker ) lasciare selezionata l'opzione Vuoto e selezionare OK.

Configurare il codice del server

In questa sezione viene configurato il codice eseguito nel server.

Creare la classe Stock

Per iniziare, creare la classe modello Stock che verrà usata per archiviare e trasmettere informazioni su un titolo.

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

  2. Assegnare alla classe il nome Stock e aggiungerlo al progetto.

  3. Sostituire il codice nel file Stock.cs con questo codice:

    using System;
    
    namespace SignalR.StockTicker
    {
        public class Stock
        {
            private decimal _price;
    
            public string Symbol { get; set; }
    
            public decimal Price
            {
                get
                {
                    return _price;
                }
                set
                {
                    if (_price == value)
                    {
                        return;
                    }
    
                    _price = value;
    
                    if (DayOpen == 0)
                    {
                        DayOpen = _price;
                    }
                }
            }
    
            public decimal DayOpen { get; private set; }
    
            public decimal Change
            {
                get
                {
                    return Price - DayOpen;
                }
            }
    
            public double PercentChange
            {
                get
                {
                    return (double)Math.Round(Change / Price, 4);
                }
            }
        }
    }
    

    Le due proprietà che verranno impostate quando si creano azioni sono Symbol (ad esempio, MSFT per Microsoft) e Price. Le altre proprietà dipendono da come e quando si imposta Price. La prima volta che si imposta Price, il valore viene propagato a DayOpen. Successivamente, quando si imposta Price, l'app calcola i valori delle Change proprietà e PercentChange in base alla differenza tra Price e DayOpen.

Creare le classi StockTickerHub e StockTicker

Si userà l'API dell'hub SignalR per gestire l'interazione da server a client. Una StockTickerHub classe che deriva dalla classe SignalR Hub gestirà la ricezione di connessioni e chiamate di metodo dai client. È anche necessario mantenere i dati azionari ed eseguire un Timer oggetto . L'oggetto Timer attiverà periodicamente gli aggiornamenti dei prezzi indipendentemente dalle connessioni client. Non è possibile inserire queste funzioni in una Hub classe, perché Hub sono temporanei. L'app crea un'istanza Hub di classe per ogni attività nell'hub, ad esempio connessioni e chiamate dal client al server. Quindi il meccanismo che mantiene i dati azionari, aggiorna i prezzi e trasmette gli aggiornamenti dei prezzi deve essere eseguito in una classe separata. Si chiamerà la classe StockTicker.

Trasmissione da StockTicker

Si vuole eseguire una sola istanza della StockTicker classe nel server, quindi è necessario configurare un riferimento da ogni StockTickerHub istanza all'istanza singleton StockTicker . La StockTicker classe deve trasmettere ai client perché contiene i dati azionari e attiva gli aggiornamenti, ma StockTicker non è una Hub classe. La StockTicker classe deve ottenere un riferimento all'oggetto contesto di connessione dell'hub SignalR. Può quindi usare l'oggetto contesto di connessione SignalR per trasmettere ai client.

Creare StockTickerHub.cs

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

  2. In Aggiungi nuovo elemento - SignalR.StockTicker selezionare VisualC#>Web>SignalRinstallato> e quindi selezionare Classe hub SignalR (v2).

  3. Assegnare alla classe il nome StockTickerHub e aggiungerlo al progetto.

    Questo passaggio crea il file di classe StockTickerHub.cs . Aggiunge contemporaneamente un set di file di script e riferimenti ad assembly che supporta SignalR al progetto.

  4. Sostituire il codice nel file StockTickerHub.cs con questo codice:

    using System.Collections.Generic;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace SignalR.StockTicker
    {
        [HubName("stockTickerMini")]
        public class StockTickerHub : Hub
        {
            private readonly StockTicker _stockTicker;
    
            public StockTickerHub() : this(StockTicker.Instance) { }
    
            public StockTickerHub(StockTicker stockTicker)
            {
                _stockTicker = stockTicker;
            }
    
            public IEnumerable<Stock> GetAllStocks()
            {
                return _stockTicker.GetAllStocks();
            }
        }
    }
    
  5. Salvare il file.

L'app usa la classe Hub per definire i metodi che i client possono chiamare sul server. Si sta definendo un metodo: GetAllStocks(). Quando un client si connette inizialmente al server, chiamerà questo metodo per ottenere un elenco di tutti gli stock con i prezzi correnti. Il metodo può essere eseguito in modo sincrono e restituito IEnumerable<Stock> perché restituisce dati dalla memoria.

Se il metodo deve ottenere i dati eseguendo un'operazione che comporta l'attesa, ad esempio una ricerca di database o una chiamata al servizio Web, è necessario specificare Task<IEnumerable<Stock>> come valore restituito per abilitare l'elaborazione asincrona. Per altre informazioni, vedere ASP.NET Guida api hub SignalR - Server - Quando eseguire in modo asincrono.

L'attributo HubName specifica come l'app farà riferimento all'hub nel codice JavaScript nel client. Il nome predefinito nel client, se non si usa questo attributo, è una versione camelCase del nome della classe, che in questo caso sarà stockTickerHub.

Come si vedrà più avanti quando si crea la StockTicker classe, l'app crea un'istanza singleton di tale classe nella relativa proprietà statica Instance . L'istanza singleton di StockTicker è in memoria indipendentemente dal numero di client che si connettono o si disconnettono. Tale istanza è ciò che il GetAllStocks() metodo usa per restituire le informazioni sulle scorte correnti.

Creare StockTicker.cs

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

  2. Assegnare alla classe il nome StockTicker e aggiungerlo al progetto.

  3. Sostituire il codice nel file StockTicker.cs con questo codice:

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace SignalR.StockTicker
    {
        public class StockTicker
        {
            // Singleton instance
            private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
    
            private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
    
            private readonly object _updateStockPricesLock = new object();
    
            //stock can go up or down by a percentage of this factor on each change
            private readonly double _rangePercent = .002;
    
            private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
            private readonly Random _updateOrNotRandom = new Random();
    
            private readonly Timer _timer;
            private volatile bool _updatingStockPrices = false;
    
            private StockTicker(IHubConnectionContext<dynamic> clients)
            {
                Clients = clients;
    
                _stocks.Clear();
                var stocks = new List<Stock>
                {
                    new Stock { Symbol = "MSFT", Price = 30.31m },
                    new Stock { Symbol = "APPL", Price = 578.18m },
                    new Stock { Symbol = "GOOG", Price = 570.30m }
                };
                stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));
    
                _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
    
            }
    
            public static StockTicker Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
    
            private IHubConnectionContext<dynamic> Clients
            {
                get;
                set;
            }
    
            public IEnumerable<Stock> GetAllStocks()
            {
                return _stocks.Values;
            }
    
            private void UpdateStockPrices(object state)
            {
                lock (_updateStockPricesLock)
                {
                    if (!_updatingStockPrices)
                    {
                        _updatingStockPrices = true;
    
                        foreach (var stock in _stocks.Values)
                        {
                            if (TryUpdateStockPrice(stock))
                            {
                                BroadcastStockPrice(stock);
                            }
                        }
    
                        _updatingStockPrices = false;
                    }
                }
            }
    
            private bool TryUpdateStockPrice(Stock stock)
            {
                // Randomly choose whether to update this stock or not
                var r = _updateOrNotRandom.NextDouble();
                if (r > .1)
                {
                    return false;
                }
    
                // Update the stock price by a random factor of the range percent
                var random = new Random((int)Math.Floor(stock.Price));
                var percentChange = random.NextDouble() * _rangePercent;
                var pos = random.NextDouble() > .51;
                var change = Math.Round(stock.Price * (decimal)percentChange, 2);
                change = pos ? change : -change;
    
                stock.Price += change;
                return true;
            }
    
            private void BroadcastStockPrice(Stock stock)
            {
                Clients.All.updateStockPrice(stock);
            }
    
        }
    }
    

Poiché tutti i thread eseguono la stessa istanza del codice StockTicker, la classe StockTicker deve essere thread-safe.

Esaminare il codice del server

Se si esamina il codice del server, sarà utile comprendere il funzionamento dell'app.

Archiviazione dell'istanza singleton in un campo statico

Il codice inizializza il campo statico _instance che esegue il backup della Instance proprietà con un'istanza della classe . Poiché il costruttore è privato, è l'unica istanza della classe che l'app può creare. L'app usa l'inizializzazione differita per il _instance campo. Non è per motivi di prestazioni. È necessario assicurarsi che la creazione dell'istanza sia thread-safe.

private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));

public static StockTicker Instance
{
    get
    {
        return _instance.Value;
    }
}

Ogni volta che un client si connette al server, una nuova istanza della classe StockTickerHub in esecuzione in un thread separato ottiene l'istanza singleton di StockTicker dalla StockTicker.Instance proprietà statica, come illustrato in precedenza nella StockTickerHub classe .

Archiviazione dei dati azionari in un oggetto ConcurrentDictionary

Il costruttore inizializza la _stocks raccolta con alcuni dati azionari di esempio e GetAllStocks restituisce gli stock. Come illustrato in precedenza, questa raccolta di azioni viene restituita da StockTickerHub.GetAllStocks, che è un metodo server nella Hub classe che i client possono chiamare.

private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
private StockTicker(IHubConnectionContext<dynamic> clients)
{
    Clients = clients;

    _stocks.Clear();
    var stocks = new List<Stock>
    {
        new Stock { Symbol = "MSFT", Price = 30.31m },
        new Stock { Symbol = "APPL", Price = 578.18m },
        new Stock { Symbol = "GOOG", Price = 570.30m }
    };
    stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));

    _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
}

public IEnumerable<Stock> GetAllStocks()
{
    return _stocks.Values;
}

La raccolta di titoli è definita come tipo ConcurrentDictionary per thread safety. In alternativa, è possibile usare un oggetto Dictionary e bloccare in modo esplicito il dizionario quando si apportano modifiche.

Per questa applicazione di esempio, è possibile archiviare i dati dell'applicazione in memoria e perdere i dati quando l'app elimina l'istanza StockTicker . In un'applicazione reale si usa un archivio dati back-end come un database.

Aggiornamento periodico dei prezzi azionari

Il costruttore avvia un Timer oggetto che chiama periodicamente metodi che aggiornano i prezzi delle azioni in modo casuale.

_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);

private void UpdateStockPrices(object state)
{
    lock (_updateStockPricesLock)
    {
        if (!_updatingStockPrices)
        {
            _updatingStockPrices = true;

            foreach (var stock in _stocks.Values)
            {
                if (TryUpdateStockPrice(stock))
                {
                    BroadcastStockPrice(stock);
                }
            }

            _updatingStockPrices = false;
        }
    }
}

private bool TryUpdateStockPrice(Stock stock)
{
    // Randomly choose whether to update this stock or not
    var r = _updateOrNotRandom.NextDouble();
    if (r > .1)
    {
        return false;
    }

    // Update the stock price by a random factor of the range percent
    var random = new Random((int)Math.Floor(stock.Price));
    var percentChange = random.NextDouble() * _rangePercent;
    var pos = random.NextDouble() > .51;
    var change = Math.Round(stock.Price * (decimal)percentChange, 2);
    change = pos ? change : -change;

    stock.Price += change;
    return true;
}

Timer chiama UpdateStockPrices, che passa null nel parametro di stato. Prima di aggiornare i prezzi, l'app assume un blocco sull'oggetto _updateStockPricesLock . Il codice controlla se un altro thread sta già aggiornando i prezzi e quindi chiama TryUpdateStockPrice su ogni stock nell'elenco. Il TryUpdateStockPrice metodo decide se modificare il prezzo azionario e quanto modificarlo. Se il prezzo azionario cambia, l'app chiama BroadcastStockPrice per trasmettere la modifica del prezzo azionario a tutti i clienti connessi.

Flag _updatingStockPrices designato volatile per assicurarsi che sia thread-safe.

private volatile bool _updatingStockPrices = false;

In un'applicazione reale, il TryUpdateStockPrice metodo chiamerebbe un servizio Web per cercare il prezzo. In questo codice, l'app usa un generatore di numeri casuali per apportare modifiche in modo casuale.

Recupero del contesto SignalR in modo che la classe StockTicker possa trasmettere ai client

Poiché le modifiche al prezzo hanno origine qui nell'oggetto StockTicker , è l'oggetto che deve chiamare un updateStockPrice metodo su tutti i client connessi. In una Hub classe è disponibile un'API per chiamare i metodi client, ma StockTicker non deriva dalla Hub classe e non ha un riferimento ad alcun Hub oggetto. Per trasmettere ai client connessi, la StockTicker classe deve ottenere l'istanza del contesto SignalR per la StockTickerHub classe e usarla per chiamare i metodi nei client.

Il codice ottiene un riferimento al contesto SignalR quando crea l'istanza della classe singleton, passa tale riferimento al costruttore e il costruttore lo inserisce nella Clients proprietà .

Ci sono due motivi per cui vuoi ottenere il contesto una sola volta: ottenere il contesto è un'attività costosa e ottenere una volta che l'app mantiene l'ordine previsto dei messaggi inviati ai client.

private readonly static Lazy<StockTicker> _instance =
    new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));

private StockTicker(IHubConnectionContext<dynamic> clients)
{
    Clients = clients;

    // Remainder of constructor ...
}

private IHubConnectionContext<dynamic> Clients
{
    get;
    set;
}

private void BroadcastStockPrice(Stock stock)
{
    Clients.All.updateStockPrice(stock);
}

Ottenere la Clients proprietà del contesto e inserirla nella StockTickerClient proprietà consente di scrivere codice per chiamare i metodi client che hanno lo stesso aspetto di una Hub classe. Ad esempio, per trasmettere a tutti i client è possibile scrivere Clients.All.updateStockPrice(stock).

Il updateStockPrice metodo che si sta chiamando in BroadcastStockPrice non esiste ancora. Verrà aggiunto in un secondo momento quando si scrive codice che viene eseguito nel client. È possibile fare riferimento qui updateStockPrice perché Clients.All è dinamico, il che significa che l'app valuterà l'espressione in fase di esecuzione. Quando questa chiamata al metodo viene eseguita, SignalR invierà il nome del metodo e il valore del parametro al client e, se il client ha un metodo denominato updateStockPrice, l'app chiamerà tale metodo e passerà il valore del parametro.

Clients.All significa inviare a tutti i client. SignalR offre altre opzioni per specificare a quali client o gruppi di client inviare. Per altre informazioni, vedere HubConnectionContext.

Registrare la route SignalR

Il server deve sapere quale URL intercettare e indirizzare a SignalR. A tale scopo, aggiungere una classe di avvio OWIN:

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

  2. In Aggiungi nuovo elemento - SignalR.StockTicker selezionareVisual C#>Webinstallato> 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 System;
    using System.Threading.Tasks;
    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(SignalR.StockTicker.Startup))]
    
    namespace SignalR.StockTicker
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
    
        }
    }
    

La configurazione del codice del server è stata completata. Nella sezione successiva si configurerà il client.

Configurare il codice client

In questa sezione viene configurato il codice eseguito nel client.

Creare la pagina HTML e il file JavaScript

La pagina HTML visualizzerà i dati e il file JavaScript organizzerà i dati.

Creare StockTicker.html

Prima di tutto, si aggiungerà il client HTML.

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

  2. Assegnare al file il nome StockTicker e selezionare OK.

  3. Sostituire il codice predefinito nel file StockTicker.html con questo codice:

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>ASP.NET SignalR Stock Ticker</title>
        <style>
            body {
                font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
                font-size: 16px;
            }
            #stockTable table {
                border-collapse: collapse;
            }
                #stockTable table th, #stockTable table td {
                    padding: 2px 6px;
                }
                #stockTable table td {
                    text-align: right;
                }
            #stockTable .loading td {
                text-align: left;
            }
        </style>
    </head>
    <body>
        <h1>ASP.NET SignalR Stock Ticker Sample</h1>
    
        <h2>Live Stock Table</h2>
        <div id="stockTable">
            <table border="1">
                <thead>
                    <tr><th>Symbol</th><th>Price</th><th>Open</th><th>Change</th><th>%</th></tr>
                </thead>
                <tbody>
                    <tr class="loading"><td colspan="5">loading...</td></tr>
                </tbody>
            </table>
        </div>
    
        <!--Script references. -->
        <!--Reference the jQuery library. -->
        <script src="/Scripts/jquery-1.10.2.min.js" ></script>
        <!--Reference the SignalR library. -->
        <script src="/Scripts/jquery.signalR-2.1.0.js"></script>
        <!--Reference the autogenerated SignalR hub script. -->
        <script src="/signalr/hubs"></script>
        <!--Reference the StockTicker script. -->
        <script src="StockTicker.js"></script>
    </body>
    </html>
    

    Il codice HTML crea una tabella con cinque colonne, una riga di intestazione e una riga di dati con una singola cella che si estende su tutte e cinque le colonne. La riga di dati mostra "caricamento..." momentaneamente all'avvio dell'app. Il codice JavaScript rimuoverà tale riga e aggiungerà al suo posto le righe con i dati di magazzino recuperati dal server.

    I tag di script specificano:

    • File di script jQuery.

    • File di script core signalR.

    • File di script proxy SignalR.

    • Un file di script StockTicker che verrà creato in un secondo momento.

    L'app genera dinamicamente il file di script proxy SignalR. Specifica l'URL "/signalr/hubs" e definisce i metodi proxy per i metodi nella classe Hub, in questo caso, per StockTickerHub.GetAllStocks. Se si preferisce, è possibile generare questo file JavaScript manualmente usando Le utilità SignalR. Non dimenticare di disabilitare la creazione dinamica dei file nella chiamata al MapHubs metodo.

  4. In Esplora soluzioni espandere Script.

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

    Importante

    Gestione pacchetti installerà una versione successiva degli script SignalR.

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

  6. In Esplora soluzioni fare clic con il pulsante destro del mouse suStockTicker.htmle quindi scegliere Imposta come pagina iniziale.

Creare StockTicker.js

Creare ora il file JavaScript.

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

  2. Assegnare al file il nome StockTicker e selezionare OK.

  3. Aggiungere questo codice al file StockTicker.js :

    // A simple templating method for replacing placeholders enclosed in curly braces.
    if (!String.prototype.supplant) {
        String.prototype.supplant = function (o) {
            return this.replace(/{([^{}]*)}/g,
                function (a, b) {
                    var r = o[b];
                    return typeof r === 'string' || typeof r === 'number' ? r : a;
                }
            );
        };
    }
    
    $(function () {
    
        var ticker = $.connection.stockTickerMini, // the generated client-side hub proxy
            up = '▲',
            down = '▼',
            $stockTable = $('#stockTable'),
            $stockTableBody = $stockTable.find('tbody'),
            rowTemplate = '<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{DayOpen}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>';
    
        function formatStock(stock) {
            return $.extend(stock, {
                Price: stock.Price.toFixed(2),
                PercentChange: (stock.PercentChange * 100).toFixed(2) + '%',
                Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down
            });
        }
    
        function init() {
            ticker.server.getAllStocks().done(function (stocks) {
                $stockTableBody.empty();
                $.each(stocks, function () {
                    var stock = formatStock(this);
                    $stockTableBody.append(rowTemplate.supplant(stock));
                });
            });
        }
    
        // Add a client-side hub method that the server will call
        ticker.client.updateStockPrice = function (stock) {
            var displayStock = formatStock(stock),
                $row = $(rowTemplate.supplant(displayStock));
    
            $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
                .replaceWith($row);
            }
    
        // Start the connection
        $.connection.hub.start().done(init);
    
    });
    

Esaminare il codice client

Se si esamina il codice client, si apprenderà come il codice client interagisce con il codice del server per rendere l'app funzionante.

Avvio della connessione

$.connection fa riferimento ai proxy SignalR. Il codice ottiene un riferimento al proxy per la StockTickerHub classe e lo inserisce nella ticker variabile . Il nome del proxy è il nome impostato dall'attributo HubName :

var ticker = $.connection.stockTickerMini
[HubName("stockTickerMini")]
public class StockTickerHub : Hub

Dopo aver definito tutte le variabili e le funzioni, l'ultima riga di codice nel file inizializza la connessione SignalR chiamando la funzione SignalR start . La start funzione viene eseguita in modo asincrono e restituisce un oggetto jQuery Deferred. È possibile chiamare la funzione done per specificare la funzione da chiamare quando l'app termina l'azione asincrona.

$.connection.hub.start().done(init);

Ottenere tutte le scorte

La init funzione chiama la getAllStocks funzione nel server e usa le informazioni restituite dal server per aggiornare la tabella di magazzino. Si noti che, per impostazione predefinita, è necessario usare camelCasing nel client anche se il nome del metodo è con maiuscole e minuscole pascal nel server. La regola camelCasing si applica solo ai metodi, non agli oggetti. Ad esempio, si fa riferimento a stock.Symbol e stock.Price, non stock.symbol o stock.price.

function init() {
    ticker.server.getAllStocks().done(function (stocks) {
        $stockTableBody.empty();
        $.each(stocks, function () {
            var stock = formatStock(this);
            $stockTableBody.append(rowTemplate.supplant(stock));
        });
    });
}
public IEnumerable<Stock> GetAllStocks()
{
    return _stockTicker.GetAllStocks();
}

init Nel metodo l'app crea codice HTML per una riga di tabella per ogni oggetto stock ricevuto dal server chiamando formatStock per formattare le proprietà dell'oggetto e quindi chiamando supplant per sostituire i segnaposto nella rowTemplate variabile con i valori della proprietà dell'oggetto stockstock. Il codice HTML risultante viene quindi aggiunto alla tabella stock.

Nota

Viene chiamato init passandolo come callback funzione che viene eseguita al termine della funzione asincrona start . Se è stata chiamata come istruzione JavaScript separata dopo aver chiamato initstart, la funzione ha esito negativo perché viene eseguita immediatamente senza attendere che la funzione di avvio finisca di stabilire la connessione. In tal caso, la init funzione tenterebbe di chiamare la getAllStocks funzione prima che l'app stabilisca una connessione server.

Ottenere i prezzi aggiornati delle azioni

Quando il server modifica il prezzo di un titolo, chiama su updateStockPrice client connessi. L'app aggiunge la funzione alla proprietà client del stockTicker proxy per renderla disponibile per le chiamate dal server.

ticker.client.updateStockPrice = function (stock) {
    var displayStock = formatStock(stock),
        $row = $(rowTemplate.supplant(displayStock));

    $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
        .replaceWith($row);
    }

La updateStockPrice funzione formatta un oggetto stock ricevuto dal server in una riga di tabella esattamente come nella init funzione. Anziché accodare la riga alla tabella, trova la riga corrente del titolo nella tabella e sostituisce tale riga con quella nuova.

Test dell'applicazione

È possibile testare l'app per assicurarsi che funzioni. Verranno visualizzate tutte le finestre del browser che visualizzano la tabella delle azioni live con prezzi azionari fluttuanti.

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

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

    Verrà aperta una finestra del browser in cui è visualizzata la tabella delle scorte aperte. La tabella delle scorte mostra inizialmente il "caricamento..." riga, quindi, dopo un breve periodo di tempo, l'app mostra i dati iniziali delle azioni e quindi i prezzi azionari iniziano a cambiare.

  2. Copiare l'URL dal browser, aprire altri due browser e incollare gli URL nelle barre degli indirizzi.

    La visualizzazione iniziale delle scorte è la stessa del primo browser e le modifiche vengono eseguite contemporaneamente.

  3. Chiudere tutti i browser, aprire un nuovo browser e passare allo stesso URL.

    L'oggetto singleton StockTicker ha continuato a essere eseguito nel server. La tabella dei titoli live mostra che le azioni hanno continuato a cambiare. La tabella iniziale non viene visualizzata con cifre di modifica zero.

  4. Chiudere il browser.

Abilitazione della registrazione

SignalR dispone di una funzione di registrazione predefinita che è possibile abilitare nel client per facilitare la risoluzione dei problemi. In questa sezione viene abilitata la registrazione e vengono visualizzati esempi che illustrano in che modo i log indicano quali dei metodi di trasporto seguenti usano SignalR:

Per qualsiasi connessione specificata, SignalR sceglie il metodo di trasporto migliore che sia il server che il client supportano.

  1. Aprire StockTicker.js.

  2. Aggiungere questa riga di codice evidenziata per abilitare la registrazione immediatamente prima del codice che inizializza la connessione alla fine del file:

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  3. Premere F5 per eseguire il progetto.

  4. Aprire la finestra degli strumenti di sviluppo del browser e selezionare la console per visualizzare i log. Potrebbe essere necessario aggiornare la pagina per visualizzare i log di SignalR che negoziano il metodo di trasporto per una nuova connessione.

    • Se si esegue Internet Explorer 10 in Windows 8 (IIS 8), il metodo di trasporto è WebSocket.

    • Se si esegue Internet Explorer 10 in Windows 7 (IIS 7.5), il metodo di trasporto è iframe.

    • Se si esegue Firefox 19 in Windows 8 (IIS 8), il metodo di trasporto è WebSockets.

      Suggerimento

      In Firefox installare il componente aggiuntivo Firebug per ottenere una finestra della console.

    • Se si esegue Firefox 19 in Windows 7 (IIS 7.5), il metodo di trasporto è eventi inviati dal server .

Installare l'esempio StockTicker

Microsoft.AspNet.SignalR.Sample installa l'applicazione StockTicker. Il pacchetto NuGet include più funzionalità rispetto alla versione semplificata creata da zero. In questa sezione dell'esercitazione viene installato il pacchetto NuGet e vengono esaminate le nuove funzionalità e il codice che li implementa.

Importante

Se si installa il pacchetto senza eseguire i passaggi precedenti di questa esercitazione, è necessario aggiungere una classe di avvio OWIN al progetto. Questo readme.txt file per il pacchetto NuGet illustra questo passaggio.

Installare il pacchetto NuGet SignalR.Sample

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Gestisci pacchetti NuGet.

  2. In Gestione pacchetti NuGet: SignalR.StockTicker selezionare Sfoglia.

  3. In Origine pacchetto selezionare nuget.org.

  4. Immettere SignalR.Sample nella casella di ricerca e selezionare Installazione microsoft.AspNet.SignalR.Sample>.

  5. In Esplora soluzioni espandere la cartella SignalR.Sample.

    L'installazione del pacchetto SignalR.Sample ha creato la cartella e il relativo contenuto.

  6. Nella cartella SignalR.Sample fare clic con il pulsante destro del mouse suStockTicker.htmle quindi scegliere Imposta come pagina iniziale.

    Nota

    L'installazione del pacchetto NuGet SignalR.Sample potrebbe modificare la versione di jQuery presente nella cartella Scripts . Il nuovo file StockTicker.html installato dal pacchetto nella cartella SignalR.Sample sarà sincronizzato con la versione jQuery installata dal pacchetto, ma se si vuole eseguire nuovamente il file diStockTicker.html originale, potrebbe essere necessario aggiornare prima il riferimento jQuery nel tag script.

Eseguire l'applicazione

La tabella visualizzata nella prima app include funzionalità utili. L'applicazione ticker completa mostra nuove funzionalità: una finestra di scorrimento orizzontale che mostra i dati azionari e le azioni che cambiano colore man mano che aumentano e diminuiscono.

  1. Premere F5 per eseguire l'app.

    Quando si esegue l'app per la prima volta, il "mercato" è "chiuso" e viene visualizzata una tabella statica e una finestra ticker che non scorre.

  2. Selezionare Apri mercato.

    Screenshot del ticker live.

    • La casella Live Stock Ticker inizia a scorrere orizzontalmente e il server inizia a trasmettere periodicamente le variazioni dei prezzi delle azioni su base casuale.

    • Ogni volta che un prezzo azionario cambia, l'app aggiorna sia la tabella azionaria live che il Live Stock Ticker.

    • Quando la variazione del prezzo di un titolo è positiva, l'app mostra il titolo con uno sfondo verde.

    • Quando la modifica è negativa, l'app mostra il titolo con uno sfondo rosso.

  3. Selezionare Chiudi mercato.

    • La tabella viene aggiornata.

    • Il ticker smette di scorrere.

  4. Selezionare Reimposta.

    • Tutti i dati azionari vengono reimpostati.

    • L'app ripristina lo stato iniziale prima dell'avvio delle modifiche al prezzo.

  5. Copiare l'URL dal browser, aprire altri due browser e incollare gli URL nelle barre degli indirizzi.

  6. In ogni browser vengono visualizzati gli stessi dati aggiornati dinamicamente.

  7. Quando si seleziona uno dei controlli, tutti i browser rispondono allo stesso modo contemporaneamente.

Visualizzazione di Live Stock Ticker

La visualizzazione Live Stock Ticker è un elenco non ordinato in un elemento formattato in una <div> singola riga in base agli stili CSS. L'app inizializza e aggiorna il ticker allo stesso modo della tabella: sostituendo i segnaposto in una <li> stringa di modello e aggiungendo dinamicamente gli <li> elementi all'elemento <ul> . L'app include lo scorrimento usando la funzione jQuery animate per variare il margine sinistro dell'elenco non ordinato all'interno di <div>.

StockTicker.html SignalR.Sample

Il codice HTML ticker stock:

<h2>Live Stock Ticker</h2>
<div id="stockTicker">
    <div class="inner">
        <ul>
            <li class="loading">loading...</li>
        </ul>
    </div>
</div>

SignalR.Sample StockTicker.css

Codice CSS di ticker azionario:

#stockTicker {
    overflow: hidden;
    width: 450px;
    height: 24px;
    border: 1px solid #999;
    }

    #stockTicker .inner {
        width: 9999px;
    }

    #stockTicker ul {
        display: inline-block;
        list-style-type: none;
        margin: 0;
        padding: 0;
    }

    #stockTicker li {
        display: inline-block;
        margin-right: 8px;   
    }

    /*<li data-symbol="{Symbol}"><span class="symbol">{Symbol}</span><span class="price">{Price}</span><span class="change">{PercentChange}</span></li>*/
    #stockTicker .symbol {
        font-weight: bold;
    }

    #stockTicker .change {
        font-style: italic;
    }

SignalR.StockTicker.js SignalR.Sample

Il codice jQuery che lo rende scorrevole:

function scrollTicker() {
    var w = $stockTickerUl.width();
    $stockTickerUl.css({ marginLeft: w });
    $stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
}

Metodi aggiuntivi nel server che il client può chiamare

Per aggiungere flessibilità all'app, sono disponibili metodi aggiuntivi che l'app può chiamare.

SignalR.Sample StockTickerHub.cs

La StockTickerHub classe definisce quattro metodi aggiuntivi che il client può chiamare:

public string GetMarketState()
{
    return _stockTicker.MarketState.ToString();
}

public void OpenMarket()
{
    _stockTicker.OpenMarket();
}

public void CloseMarket()
{
    _stockTicker.CloseMarket();
}

public void Reset()
{
    _stockTicker.Reset();
}

L'app chiama OpenMarket, CloseMarkete Reset in risposta ai pulsanti nella parte superiore della pagina. Illustrano il modello di un client che attiva una modifica dello stato immediatamente propagato a tutti i client. Ognuno di questi metodi chiama un metodo nella StockTicker classe che causa la modifica dello stato del mercato e quindi trasmette il nuovo stato.

SignalR.Sample StockTicker.cs

StockTicker Nella classe l'app mantiene lo stato del mercato con una MarketState proprietà che restituisce un MarketState valore enumerazione:

public MarketState MarketState
{
    get { return _marketState; }
    private set { _marketState = value; }
}

public enum MarketState
{
    Closed,
    Open
}

Ognuno dei metodi che modificano lo stato del mercato lo fa all'interno di un blocco di blocco perché la StockTicker classe deve essere thread-safe:

public void OpenMarket()
{
    lock (_marketStateLock)
    {
        if (MarketState != MarketState.Open)
        {
            _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
            MarketState = MarketState.Open;
            BroadcastMarketStateChange(MarketState.Open);
        }
    }
}

public void CloseMarket()
{
    lock (_marketStateLock)
    {
        if (MarketState == MarketState.Open)
        {
            if (_timer != null)
            {
                _timer.Dispose();
            }
            MarketState = MarketState.Closed;
            BroadcastMarketStateChange(MarketState.Closed);
        }
    }
}

public void Reset()
{
    lock (_marketStateLock)
    {
        if (MarketState != MarketState.Closed)
        {
            throw new InvalidOperationException("Market must be closed before it can be reset.");
        }
        LoadDefaultStocks();
        BroadcastMarketReset();
    }
}

Per assicurarsi che questo codice sia thread-safe, il campo che esegue il _marketState backup della MarketState proprietà designata volatile:

private volatile MarketState _marketState;

I BroadcastMarketStateChange metodi e BroadcastMarketReset sono simili al metodo BroadcastStockPrice già visto, ad eccezione del fatto che chiamano metodi diversi definiti nel client:

private void BroadcastMarketStateChange(MarketState marketState)
{
    switch (marketState)
    {
        case MarketState.Open:
            Clients.All.marketOpened();
            break;
        case MarketState.Closed:
            Clients.All.marketClosed();
            break;
        default:
            break;
    }
}

private void BroadcastMarketReset()
{
    Clients.All.marketReset();
}

Funzioni aggiuntive nel client che il server può chiamare

La updateStockPrice funzione ora gestisce sia la tabella che la visualizzazione ticker e usa jQuery.Color per lampeggiare i colori rosso e verde.

Nuove funzioni in SignalR.StockTicker.js abilitare e disabilitare i pulsanti in base allo stato del mercato. Arrestano o avviano lo scorrimento orizzontale live stock Ticker . Poiché molte funzioni vengono aggiunte a ticker.client, l'app usa la funzione extend jQuery per aggiungerle.

$.extend(ticker.client, {
    updateStockPrice: function (stock) {
        var displayStock = formatStock(stock),
            $row = $(rowTemplate.supplant(displayStock)),
            $li = $(liTemplate.supplant(displayStock)),
            bg = stock.LastChange === 0
                ? '255,216,0' // yellow
                : stock.LastChange > 0
                    ? '154,240,117' // green
                    : '255,148,148'; // red

        $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
            .replaceWith($row);
        $stockTickerUl.find('li[data-symbol=' + stock.Symbol + ']')
            .replaceWith($li);

        $row.flash(bg, 1000);
        $li.flash(bg, 1000);
    },

    marketOpened: function () {
        $("#open").prop("disabled", true);
        $("#close").prop("disabled", false);
        $("#reset").prop("disabled", true);
        scrollTicker();
    },

    marketClosed: function () {
        $("#open").prop("disabled", false);
        $("#close").prop("disabled", true);
        $("#reset").prop("disabled", false);
        stopTicker();
    },

    marketReset: function () {
        return init();
    }
});

Configurazione client aggiuntiva dopo aver stabilito la connessione

Dopo aver stabilito la connessione, il client ha alcune operazioni aggiuntive da eseguire:

  • Scopri se il mercato è aperto o chiuso per chiamare la funzione o marketClosed appropriatamarketOpened.

  • Collegare le chiamate al metodo server ai pulsanti.

$.connection.hub.start()
    .pipe(init)
    .pipe(function () {
        return ticker.server.getMarketState();
    })
    .done(function (state) {
        if (state === 'Open') {
            ticker.client.marketOpened();
        } else {
            ticker.client.marketClosed();
        }

        // Wire up the buttons
        $("#open").click(function () {
            ticker.server.openMarket();
        });

        $("#close").click(function () {
            ticker.server.closeMarket();
        });

        $("#reset").click(function () {
            ticker.server.reset();
        });
    });

I metodi del server non vengono collegati ai pulsanti fino a quando l'app non stabilisce la connessione. È quindi possibile che il codice non possa chiamare i metodi del server prima che siano disponibili.

Risorse aggiuntive

In questa esercitazione si è appreso come programmare un'applicazione SignalR che trasmette i messaggi dal server a tutti i client connessi. Ora è possibile trasmettere i messaggi su base periodica e in risposta alle notifiche da qualsiasi client. È possibile usare il concetto di istanza singleton multithread per mantenere lo stato del server in scenari di gioco online multi-giocatore. Per un esempio, vedi il gioco ShootR basato su SignalR.

Per esercitazioni che mostrano scenari di comunicazione peer-to-peer, vedere Introduzione con SignalR e Aggiornamento in tempo reale con SignalR.

Per altre informazioni su SignalR, vedere le risorse seguenti:

Passaggi successivi

In questa esercitazione:

  • Creazione del progetto
  • Configurare il codice del server
  • Esaminare il codice del server
  • Configurare il codice client
  • Esaminare il codice client
  • Testare l'applicazione
  • Registrazione abilitata

Passare all'articolo successivo per informazioni su come creare un'applicazione Web in tempo reale che usa ASP.NET SignalR 2.