Kurz: Serverové vysílání se službou SignalR 2

Upozornění

Tato dokumentace není určená pro nejnovější verzi služby SignalR. Podívejte se na ASP.NET Core SignalR.

V tomto kurzu se dozvíte, jak vytvořit webovou aplikaci, která používá ASP.NET SignalR 2 k zajištění funkce všesměrového vysílání serveru. Serverové vysílání znamená, že server zahájí komunikaci odesílanou klientům.

Aplikace, kterou v tomto kurzu vytvoříte, simuluje burzovní ticker, což je typický scénář pro funkci serverového vysílání. Server pravidelně náhodně aktualizuje ceny akcií a vysílá aktualizace všem připojeným klientům. V prohlížeči se čísla a symboly ve sloupcích Change a % dynamicky mění v reakci na oznámení ze serveru. Pokud otevřete další prohlížeče se stejnou adresou URL, všechny zobrazují stejná data a stejné změny dat současně.

Snímek obrazovky znázorňující, jak několik webových prohlížečů zobrazuje stejná aktualizovaná data současně

V tomto kurzu jste:

  • Vytvoření projektu
  • Nastavení kódu serveru
  • Prozkoumání kódu serveru
  • Nastavení klientského kódu
  • Prozkoumání kódu klienta
  • Testování aplikace
  • Povolit protokolování

Důležité

Pokud nechcete procházet kroky sestavení aplikace, můžete balíček SignalR.Sample nainstalovat do nového projektu Empty ASP.NET Web Application. Pokud nainstalujete balíček NuGet bez provedení kroků v tomto kurzu, musíte postupovat podle pokynů v souborureadme.txt . Ke spuštění balíčku je potřeba přidat spouštěcí třídu OWIN, která volá metodu ConfigureSignalR v nainstalovaném balíčku. Pokud nepřidáte spouštěcí třídu OWIN, zobrazí se chyba. Projděte si část Instalace ukázky Nástroje StockTicker tohoto článku.

Požadavky

Vytvoření projektu

Tato část ukazuje, jak pomocí sady Visual Studio 2017 vytvořit prázdnou webovou aplikaci ASP.NET.

  1. V sadě Visual Studio vytvořte webovou aplikaci ASP.NET.

    Snímek obrazovky znázorňující, jak vytvořit webovou aplikaci ASP.NET

  2. V okně New ASP.NET Web Application - SignalR.StockTicker (Nová webová aplikace ASP.NET – SignalR.StockTicker ) ponechte vybranou možnost Empty (Prázdné) a vyberte OK.

Nastavení kódu serveru

V této části nastavíte kód, který běží na serveru.

Vytvoření třídy Stock

Začnete vytvořením třídy modelu Stock , kterou použijete k ukládání a přenosu informací o akciích.

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>třídu.

  2. Pojmenujte třídu Stock a přidejte ji do projektu.

  3. Nahraďte kód v souboru Stock.cs tímto kódem:

    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);
                }
            }
        }
    }
    

    Dvě vlastnosti, které nastavíte při vytváření akcií, jsou Symbol (například MSFT pro Microsoft) a Price. Další vlastnosti závisí na tom, jak a kdy nastavíte Price. Při prvním nastavení Pricese hodnota rozšíří na DayOpen. Když pak nastavíte Price, aplikace vypočítá Change hodnoty vlastností a PercentChange na základě rozdílu mezi Price a DayOpen.

Vytvoření tříd StockTickerHub a StockTicker

K interakci mezi servery a klienty použijete rozhraní API služby SignalR Hub. Třída StockTickerHub odvozená z třídy SignalR Hub bude zpracovávat příjem připojení a volání metod z klientů. Potřebujete také udržovat skladová data a spustit Timer objekt. Objekt Timer bude pravidelně spouštět aktualizace cen nezávisle na klientských připojeních. Tyto funkce nemůžete umístit do Hub třídy, protože centra jsou přechodná. Aplikace vytvoří Hub instanci třídy pro každý úkol v centru, například připojení a volání z klienta na server. Takže mechanismus, který uchovává data akcií, aktualizuje ceny a vysílá aktualizace cen, musí běžet v samostatné třídě. Třídu pojmenujete StockTicker.

Vysílání z Burzovního tickeru

Chcete, aby na serveru běžela pouze jedna instance StockTicker třídy, takže budete muset nastavit odkaz z každé StockTickerHub instance na jednu StockTicker instanci. Třída StockTicker musí vysílat klientům, protože obsahuje data akcií a aktivuje aktualizace, ale StockTicker není Hub třídou. Třída StockTicker musí získat odkaz na objekt kontextu připojení centra SignalR. Pak může použít objekt kontextu připojení SignalR k vysílání do klientů.

Vytvoření Souboru StockTickerHub.cs

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>novou položku.

  2. V části Přidat novou položku – SignalR.StockTicker vyberte Installed>Visual C#>Web>SignalR a pak vyberte SignalR Hub Class (v2).

  3. Pojmenujte třídu StockTickerHub a přidejte ji do projektu.

    Tento krok vytvoří soubor třídy StockTickerHub.cs . Současně do projektu přidá sadu souborů skriptů a odkazů na sestavení, které podporují SignalR.

  4. Nahraďte kód v souboru StockTickerHub.cs tímto kódem:

    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. Soubor uložte.

Aplikace používá třídu Hub k definování metod, které můžou klienti volat na serveru. Definujete jednu metodu: GetAllStocks(). Když se klient poprvé připojí k serveru, zavolá tuto metodu, aby získal seznam všech akcií s jejich aktuálními cenami. Metoda může běžet synchronně a vracet IEnumerable<Stock> se, protože vrací data z paměti.

Pokud by metoda musela získat data provedením něčeho, co by zahrnovalo čekání, jako je vyhledávání v databázi nebo volání webové služby, zadali Task<IEnumerable<Stock>> byste jako návratovou hodnotu pro povolení asynchronního zpracování. Další informace najdete v tématu Příručka ASP.NET rozhraní API služby SignalR Hubs – Server – Kdy provést asynchronně.

Atribut HubName určuje, jak bude aplikace odkazovat na centrum v kódu JavaScriptu na klientovi. Výchozí název v klientovi, pokud tento atribut nepoužijete, je verze camelCase názvu třídy, která by v tomto případě byla stockTickerHub.

Jak uvidíte později při vytváření StockTicker třídy, aplikace vytvoří jednu instanci této třídy ve své statické Instance vlastnosti. Tato jediná instance je StockTicker v paměti bez ohledu na to, kolik klientů se připojí nebo odpojí. Tato instance je to, co GetAllStocks() metoda používá k vrácení aktuálních informací o akciích.

Vytvoření Souboru StockTicker.cs

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>třídu.

  2. Pojmenujte třídu StockTicker a přidejte ji do projektu.

  3. Nahraďte kód v souboru StockTicker.cs tímto kódem:

    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);
            }
    
        }
    }
    

Vzhledem k tomu, že všechna vlákna budou spuštěna stejnou instanci kódu StockTicker, třída StockTicker musí být bezpečná pro přístup z více vláken.

Prozkoumání kódu serveru

Když prozkoumáte kód serveru, pomůže vám to pochopit, jak aplikace funguje.

Uložení instance typu singleton do statického pole

Kód inicializuje statické _instance pole, které podporuje Instance vlastnost s instancí třídy . Vzhledem k tomu, že je konstruktor soukromý, je to jediná instance třídy, kterou může aplikace vytvořit. Aplikace pro _instance pole používá lazy inicializaci. Není to kvůli výkonu. Je potřeba zajistit, aby vytváření instancí bylo bezpečné pro přístup z více vláken.

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

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

Pokaždé, když se klient připojí k serveru, nová instance StockTickerHub třídy spuštěné v samostatném vlákně získá instance StockTicker singleton ze StockTicker.Instance statické vlastnosti, jak jste viděli dříve ve StockTickerHub třídě .

Ukládání burzovních dat v ConcurrentDictionary

Konstruktor inicializuje kolekci _stocks s některými ukázkovými burzovními daty a GetAllStocks vrátí akcie. Jak jste viděli dříve, tuto kolekci akcií vrací StockTickerHub.GetAllStocksmetoda , což je metoda serveru ve Hub třídě, kterou můžou volat klienti.

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;
}

Kolekce akcie je definována jako typ ConcurrentDictionary pro bezpečnost vláken. Jako alternativu můžete použít objekt Dictionary a při provádění změn slovník explicitně uzamknout.

U této ukázkové aplikace je v pořádku ukládat data aplikace do paměti a ztratit je, když aplikace instanci odstraní StockTicker . Ve skutečné aplikaci byste pracovali s back-endovým úložištěm dat, jako je databáze.

Pravidelně se aktualizují ceny akcií

Konstruktor spustí Timer objekt, který pravidelně volá metody, které náhodně aktualizují ceny akcií.

_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 volá UpdateStockPrices, který předává hodnotu null v parametru state. Před aktualizací cen aplikace zamkne objekt._updateStockPricesLock Kód zkontroluje, jestli už neaktualizuje ceny jiné vlákno, a pak zavolá TryUpdateStockPrice na každou akcii v seznamu. Metoda TryUpdateStockPrice rozhoduje o tom, jestli se má cena akcií změnit a o kolik ji změnit. Pokud se cena akcií změní, aplikace zavolá BroadcastStockPrice , aby změnu ceny akcií vysílala všem připojeným klientům.

Příznak _updatingStockPrices označený jako nestálý , aby se zajistilo, že je bezpečný z více vláken.

private volatile bool _updatingStockPrices = false;

Ve skutečné aplikaci by metoda volala webovou službu, TryUpdateStockPrice aby vyhledala cenu. V tomto kódu aplikace používá generátor náhodných čísel k náhodnému provádění změn.

Získání kontextu SignalR, aby třída StockTicker mohl vysílat klientům

Vzhledem k tomu, že změny cen pocházejí z objektu StockTicker , je to objekt, který potřebuje volat metodu updateStockPrice na všech připojených klientech. Hub Ve třídě máte rozhraní API pro volání klientských metod, ale StockTicker neodvozuje se z Hub třídy a nemá odkaz na žádný Hub objekt. Pokud chcete vysílat do připojených klientů, StockTicker musí třída získat instanci kontextu SignalR pro StockTickerHub třídu a použít ji k volání metod na klientech.

Kód získá odkaz na kontext SignalR, když vytvoří instanci singleton třídy, předá tento odkaz konstruktoru a konstruktor ho vloží do Clients vlastnosti.

Existují dva důvody, proč chcete získat kontext jenom jednou: získání kontextu je nákladný úkol a jeho získání jednou zajistí, že aplikace zachová zamýšlené pořadí zpráv odesílaných klientům.

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);
}

Clients Získání vlastnosti kontextu a jeho vložení do StockTickerClient vlastnosti umožňuje psát kód pro volání metod klienta, které vypadají stejně jako ve Hub třídě. Pokud chcete například vysílat všechny klienty, můžete napsat Clients.All.updateStockPrice(stock).

Metoda updateStockPrice , kterou voláte BroadcastStockPrice , ještě neexistuje. Přidáte ho později při psaní kódu, který běží na klientovi. Tady můžete odkazovat, updateStockPrice protože Clients.All je dynamický, což znamená, že aplikace vyhodnotí výraz za běhu. Když se toto volání metody spustí, SignalR odešle název metody a hodnotu parametru klientovi a pokud má klient metodu s názvem updateStockPrice, aplikace zavolá tuto metodu a předá jí hodnotu parametru.

Clients.All znamená odeslat všem klientům. SignalR nabízí další možnosti, jak určit, kterým klientům nebo skupinám klientů se má odesílat. Další informace najdete v tématu HubConnectionContext.

Registrace trasy SignalR

Server potřebuje vědět, kterou adresu URL má zachytit a směrovat na SignalR. Chcete-li to provést, přidejte spouštěcí třídu OWIN:

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>novou položku.

  2. V části Přidat novou položku – SignalR.StockTicker vyberte Installed Visual C#Web ( Nainstalovaný>visual C#>Web ) a pak vyberte OWIN Startup Class (Třída spouštění OWIN).

  3. Pojmenujte třídu Startup a vyberte OK.

  4. Nahraďte výchozí kód v souboru Startup.cs tímto kódem:

    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();
            }
    
        }
    }
    

Dokončili jste nastavení kódu serveru. V další části nastavíte klienta.

Nastavení klientského kódu

V této části nastavíte kód, který běží na klientovi.

Vytvoření stránky HTML a souboru JavaScriptu

Stránka HTML zobrazí data a soubor JavaScriptu data uspořádá.

Vytvořit StockTicker.html

Nejprve přidáte klienta HTML.

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>stránku HTML.

  2. Pojmenujte soubor StockTicker a vyberte OK.

  3. Nahraďte výchozí kód v souboru StockTicker.html tímto kódem:

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

    Kód HTML vytvoří tabulku s pěti sloupci, řádkem záhlaví a datovým řádkem s jednou buňkou, která zahrnuje všech pět sloupců. Na řádku dat se zobrazuje "načítání..." v okamžiku, kdy se aplikace spustí. JavaScriptový kód odebere tento řádek a přidá na jeho místo řádky s burzovními daty načtenými ze serveru.

    Značky skriptu určují:

    • Soubor skriptu jQuery.

    • Soubor základního skriptu SignalR.

    • Soubor skriptu proxy serverů SignalR.

    • Soubor skriptu StockTicker, který vytvoříte později.

    Aplikace dynamicky generuje soubor skriptu proxy serverů SignalR. Určuje adresu URL /signalr/hubs a definuje metody proxy pro metody ve třídě Hub, v tomto případě pro StockTickerHub.GetAllStocks. Pokud chcete, můžete tento javascriptový soubor vygenerovat ručně pomocí nástrojů SignalR. Nezapomeňte při volání metody zakázat dynamické vytváření MapHubs souborů.

  4. V Průzkumník řešení rozbalte Položku Skripty.

    Knihovny skriptů pro jQuery a SignalR jsou viditelné v projektu.

    Důležité

    Správce balíčků nainstaluje novější verzi skriptů SignalR.

  5. Aktualizujte odkazy na skripty v bloku kódu tak, aby odpovídaly verzím souborů skriptů v projektu.

  6. V Průzkumník řešení klikněte pravým tlačítkem na StockTicker.htmla pak vyberte Nastavit jako úvodní stránku.

Vytvořit StockTicker.js

Teď vytvořte soubor JavaScriptu.

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat>soubor JavaScriptu.

  2. Pojmenujte soubor StockTicker a vyberte OK.

  3. Do souboru StockTicker.js přidejte tento kód:

    // 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);
    
    });
    

Prozkoumání kódu klienta

Pokud prozkoumáte kód klienta, pomůže vám to zjistit, jak kód klienta komunikuje s kódem serveru, aby aplikace fungovala.

Zahájení připojení

$.connection odkazuje na proxy služby SignalR. Kód získá odkaz na proxy pro StockTickerHub třídu a umístí ho do ticker proměnné. Název proxy serveru je název, který byl nastaven atributem HubName :

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

Po definování všech proměnných a funkcí inicializuje poslední řádek kódu v souboru připojení SignalR voláním funkce SignalR start . Funkce start provede asynchronně a vrátí objekt jQuery Deferred. Můžete volat hotovou funkci a určit funkci, která se má volat, když aplikace dokončí asynchronní akci.

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

Získání všech akcií

Funkce init volá getAllStocks funkci na serveru a použije informace, které server vrátí, k aktualizaci burzovní tabulky. Všimněte si, že ve výchozím nastavení musíte na klientovi použít camelCasing, i když je název metody na serveru s písmeny pascal. Pravidlo camelCasing se vztahuje pouze na metody, ne na objekty. Například odkazujete na stock.Symbol a stock.Price, ne stock.symbol nebo 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 V metodě aplikace vytvoří html pro řádek tabulky pro každý skladový objekt přijatý ze serveru voláním formatStock vlastností formátu objektu stock a pak voláním supplant nahradit zástupné symboly v rowTemplate proměnné hodnotami vlastností objektustock. Výsledný kód HTML se pak připojí k burzovní tabulce.

Poznámka

Zavoláte init ho předáním jako callback funkci, která se spustí po dokončení asynchronní start funkce. Pokud jste po volání startvolali init jako samostatný příkaz Jazyka JavaScript , funkce by selhala, protože by se spustila okamžitě, aniž by čekala na dokončení navazování připojení funkcí Start. V takovém případě se funkce pokusí volat getAllStocks funkci předtím, init než aplikace naváže připojení k serveru.

Získání aktualizovaných cen akcií

Když server změní cenu akcie, zavolá updateStockPrice připojené klienty. Aplikace přidá funkci do vlastnosti stockTicker klienta proxy serveru, aby byla dostupná pro volání ze serveru.

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

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

Funkce updateStockPrice formátuje akciový objekt přijatý ze serveru do řádku tabulky stejným způsobem jako ve init funkci. Místo připojení řádku k tabulce najde aktuální řádek akcie v tabulce a nahradí ho novým řádkem.

Testování aplikace

Aplikaci můžete otestovat, abyste měli jistotu, že funguje. Ve všech oknech prohlížeče se zobrazí živá burzovní tabulka s kolísajícími cenami akcií.

  1. Na panelu nástrojů zapněte Ladění skriptů a pak výběrem tlačítka Přehrát spusťte aplikaci v režimu ladění.

    Snímek obrazovky s uživatelem, který zapne režim ladění a vybere přehrát

    Otevře se okno prohlížeče s živou burzovní tabulkou. Burzovní tabulka zpočátku zobrazuje "načítání..." po krátké době aplikace zobrazí počáteční data o akciích a ceny akcií se začnou měnit.

  2. Zkopírujte adresu URL z prohlížeče, otevřete dva další prohlížeče a vložte adresy URL do adresního řádku.

    Počáteční zobrazení akcií je stejné jako v prvním prohlížeči a změny probíhají současně.

  3. Zavřete všechny prohlížeče, otevřete nový prohlížeč a přejděte na stejnou adresu URL.

    Objekt singleton StockTicker pokračoval v běhu na serveru. Tabulka živých akcií ukazuje, že se akcie stále mění. Nevidíte počáteční tabulku s nulovými údaji o změnách.

  4. Zavřete prohlížeč.

Povolit protokolování

SignalR má integrovanou funkci protokolování, kterou můžete na klientovi povolit, aby vám pomohla při řešení potíží. V této části povolíte protokolování a zobrazíte příklady, které ukazují, jak protokoly říkají, kterou z následujících metod přenosu používá SignalR:

Pro jakékoli připojení zvolí SignalR nejlepší metodu přenosu, kterou podporuje server i klient.

  1. Otevřete StockTicker.js.

  2. Přidejte tento zvýrazněný řádek kódu, který povolí protokolování bezprostředně před kódem, který inicializuje připojení na konci souboru:

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  3. Stisknutím klávesy F5 spusťte projekt.

  4. Otevřete okno vývojářských nástrojů v prohlížeči a výběrem konzoly zobrazte protokoly. Možná budete muset stránku aktualizovat, abyste viděli protokoly služby SignalR, která vyjednává metodu přenosu pro nové připojení.

    • Pokud používáte Internet Explorer 10 na Windows 8 (IIS 8), metoda přenosu je WebSockets.

    • Pokud používáte Internet Explorer 10 ve Windows 7 (IIS 7.5), metoda přenosu je iframe.

    • Pokud používáte Firefox 19 na Windows 8 (IIS 8), metoda přenosu je WebSockets.

      Tip

      Ve Firefoxu nainstalujte doplněk Firebug a získejte okno konzoly.

    • Pokud používáte Firefox 19 ve Windows 7 (IIS 7.5), metoda přenosu je události odeslané serverem .

Instalace ukázky StockTicker

Microsoft.AspNet.SignalR.Sample nainstaluje aplikaci StockTicker. Balíček NuGet obsahuje více funkcí než zjednodušená verze, kterou jste vytvořili od začátku. V této části kurzu nainstalujete balíček NuGet a zkontrolujete nové funkce a kód, který je implementuje.

Důležité

Pokud nainstalujete balíček bez provedení předchozích kroků tohoto kurzu, musíte do projektu přidat spouštěcí třídu OWIN. Tento readme.txt soubor pro balíček NuGet vysvětluje tento krok.

Instalace balíčku NuGet SignalR.Sample

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Spravovat balíčky NuGet.

  2. Ve správci balíčků NuGet: SignalR.StockTicker vyberte Procházet.

  3. V části Zdroj balíčku vyberte nuget.org.

  4. Do vyhledávacího pole zadejte SignalR.Sample a vyberte Microsoft.AspNet.SignalR.Sample>Install.

  5. V Průzkumník řešení rozbalte složku SignalR.Sample.

    Instalace balíčku SignalR.Sample vytvořila složku a její obsah.

  6. Ve složce SignalR.Sample klikněte pravým tlačítkem na StockTicker.htmla pak vyberte Nastavit jako úvodní stránku.

    Poznámka

    Instalace balíčku NuGet SignalR.Sample může změnit verzi jQuery, kterou máte ve složce Scripts . Nový souborStockTicker.html , který balíček nainstaluje do složky SignalR.Sample , bude synchronizovaný s verzí jQuery, kterou balíček nainstaluje, ale pokud chcete původní StockTicker.html soubor spustit znovu, možná budete muset nejprve aktualizovat odkaz jQuery ve značce script.

Spuštění aplikace

Tabulka, kterou jste viděli v první aplikaci, měla užitečné funkce. Úplná aplikace burzovních akcií zobrazuje nové funkce: vodorovně posouvací okno, které zobrazuje data akcií a akcie, které mění barvu, když rostou a klesají.

  1. Stisknutím klávesy F5 aplikaci spusťte.

    Při prvním spuštění aplikace se "trh" zavře a zobrazí se statická tabulka a okno s tickerem, které se neposouvá.

  2. Vyberte Otevřít trh.

    Snímek obrazovky s živým tickerem

    • Pole Live Stock Ticker se začne vodorovně posouvat a server začne pravidelně náhodně vysílat změny cen akcií.

    • Pokaždé, když se změní cena akcií, aplikace aktualizuje tabulku live stock i live stock ticker.

    • Pokud je změna ceny akcie kladná, aplikace zobrazí akcii se zeleným pozadím.

    • Pokud je změna záporná, aplikace zobrazí akcie s červeným pozadím.

  3. Vyberte Zavřít trh.

    • Tabulka se aktualizuje.

    • Ticker přestane posouvat.

  4. Vyberte Resetovat.

    • Všechna data akcií se resetuje.

    • Aplikace obnoví počáteční stav před zahájením změn cen.

  5. Zkopírujte adresu URL z prohlížeče, otevřete dva další prohlížeče a vložte adresy URL do adresního řádku.

  6. V každém prohlížeči uvidíte stejná dynamicky aktualizovaná data současně.

  7. Když vyberete některý z ovládacích prvků, všechny prohlížeče budou reagovat stejným způsobem ve stejnou dobu.

Zobrazení live stock tickeru

Zobrazení live stock tickeru je neuspořádaný seznam v elementu <div> formátovaného do jednoho řádku podle stylů CSS. Aplikace inicializuje a aktualizuje ticker stejným způsobem jako tabulka: nahrazením zástupných symbolů v <li> řetězci šablony a dynamickým přidáním <li> prvků do elementu <ul> . Aplikace zahrnuje posouvání pomocí funkce jQuery animate , která mění levý okraj neuspořádaného seznamu v rámci <div>.

StockTicker.html SignalR.Sample

Kód HTML burzovního kódu:

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

SignalR.Sample StockTicker.css

Kód CSS burzovního tickeru:

#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

Kód jQuery, který ho posouvá:

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

Další metody na serveru, které může klient volat

Pro zvýšení flexibility aplikace existují další metody, které může aplikace volat.

SignalR.Sample StockTickerHub.cs

Třída StockTickerHub definuje čtyři další metody, které může klient volat:

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

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

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

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

Aplikace volá OpenMarket, CloseMarketa Reset v reakci na tlačítka v horní části stránky. Demonstrují vzor jednoho klienta, který aktivuje změnu stavu okamžitě šířenou do všech klientů. Každá z těchto metod volá metodu StockTicker ve třídě, která způsobuje změnu stavu trhu, a pak vysílá nový stav.

SignalR.Sample StockTicker.cs

StockTicker Ve třídě aplikace udržuje stav trhu s MarketState vlastností, která vrací hodnotu výčtuMarketState:

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

public enum MarketState
{
    Closed,
    Open
}

Každá z metod, které mění stav trhu, to dělá uvnitř blok zámku, protože StockTicker třída musí být bezpečná pro přístup z více vláken:

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();
    }
}

Abyste měli jistotu_marketState, že je tento kód bezpečný pro přístup z více vláken, pole, které vrací MarketState vlastnost určenou :volatile

private volatile MarketState _marketState;

Metody BroadcastMarketStateChange a BroadcastMarketReset se podobají metodě BroadcastStockPrice, kterou jste už viděli, s tím rozdílem, že volají různé metody definované v klientovi:

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();
}

Další funkce v klientovi, které může server volat

Funkce updateStockPrice teď zpracovává tabulku i zobrazení tickeru a používá jQuery.Color k blikající červené a zelené barvě.

Nové funkce v SignalR.StockTicker.js tlačítka povolit a zakázat na základě stavu trhu. Zastaví nebo spustí vodorovné posouvání live stock tickeru . Vzhledem k tomu, že se do ticker.clientaplikace přidává mnoho funkcí, aplikace k jejich přidání používá funkci rozšíření jQuery .

$.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();
    }
});

Další nastavení klienta po navázání připojení

Jakmile klient naváže připojení, bude muset provést další práci:

  • Zjistěte, jestli je trh otevřený nebo uzavřený kvůli volání příslušné marketOpened funkce nebo marketClosed funkce.

  • Připojte volání metody serveru k tlačítkům.

$.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();
        });
    });

Metody serveru nejsou připojeny k tlačítkům, dokud aplikace nenaváže připojení. Je to proto, aby kód nemohl volat serverové metody, dokud nebudou k dispozici.

Další materiály

V tomto kurzu jste se naučili naprogramovat aplikaci SignalR, která vysílá zprávy ze serveru všem připojeným klientům. Teď můžete zprávy vysílat pravidelně a v reakci na oznámení od libovolného klienta. Koncept singletonové instance s více vlákny můžete použít k udržování stavu serveru ve scénářích online her s více hráči. Příklad: Podívejte se na hru ShootR založenou na SignalR.

Kurzy, které ukazují scénáře komunikace mezi dvěma účastníky, najdete v tématech Začínáme se službou SignalR a Aktualizace v reálném čase pomocí SignalR.

Další informace o službě SignalR najdete v následujících zdrojích informací:

Další kroky

V tomto kurzu jste:

  • Vytvořil se projekt.
  • Nastavení kódu serveru
  • Prozkoumání kódu serveru
  • Nastavení klientského kódu
  • Prověřil kód klienta.
  • Otestování aplikace
  • Povolené protokolování

V dalším článku se dozvíte, jak vytvořit webovou aplikaci v reálném čase, která používá ASP.NET SignalR 2.