Samouczek: emisja serwera z sygnałem 2

Warning

Ta dokumentacja nie jest najnowsza dla najnowszej wersji usługi sygnalizującej. Zapoznaj się z tematem ASP.NET Core sygnalizujący.

W tym samouczku pokazano, jak utworzyć aplikację sieci Web korzystającą z ASP.NET sygnalizującego 2, aby zapewnić funkcję emisji serwera. Emisja serwera oznacza, że serwer uruchamia komunikację wysyłaną do klientów.

Aplikacja, którą utworzysz w tym samouczku, symuluje taktowanie giełdowe, typowy scenariusz dla funkcji emisji serwera. Okresowo serwer losowo aktualizuje ceny akcji i emituje aktualizacje do wszystkich podłączonych klientów. W przeglądarce numery i symbole w zmianach i % kolumnach zmieniają się dynamicznie w odpowiedzi na powiadomienia z serwera. Jeśli otworzysz dodatkowe przeglądarki pod tym samym adresem URL, wszystkie będą wyświetlać te same dane i te same zmiany w danych jednocześnie.

Tworzenie sieci Web

W tym samouczku zostały wykonane następujące czynności:

  • Tworzenie projektu
  • Konfigurowanie kodu serwera
  • Sprawdzanie kodu serwera
  • Konfigurowanie kodu klienta
  • Sprawdzanie kodu klienta
  • Testowanie aplikacji
  • Włącz rejestrowanie

Important

Jeśli nie chcesz wykonać kroków tworzenia aplikacji, możesz zainstalować program sygnalizujący. przykładowego pakietu w nowym pustym projekcie aplikacji sieci Web ASP.NET. W przypadku zainstalowania pakietu NuGet bez wykonywania czynności opisanych w tym samouczku należy postępować zgodnie z instrukcjami w pliku readme.txt . Aby uruchomić pakiet, należy dodać klasę uruchomieniową OWIN, która wywołuje ConfigureSignalR metodę w zainstalowanym pakiecie. Jeśli nie dodasz klasy uruchomieniowej OWIN, zostanie wyświetlony komunikat o błędzie. Zobacz sekcję Instalowanie przykładu StockTicker w tym artykule.

Wymagania wstępne

  • Program Visual Studio 2017 z pakietem roboczym Tworzenie aplikacji na platformie ASP.NET i aplikacji internetowych.

Tworzenie projektu

W tej sekcji pokazano, jak utworzyć pustą aplikację sieci Web ASP.NET za pomocą programu Visual Studio 2017.

  1. W programie Visual Studio Utwórz aplikację sieci Web ASP.NET.

    Tworzenie sieci Web

  2. W oknie nowy ASP.NET Web Application-sygnalizującer. StockTicker pozostaw puste zaznaczone i wybierz przycisk OK.

Konfigurowanie kodu serwera

W tej sekcji skonfigurujesz kod, który jest uruchamiany na serwerze.

Tworzenie klasy giełdowej

Zacznij od utworzenia klasy modelu giełdowego , która będzie używana do przechowywania i przesyłania informacji o magazynie.

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > klasę.

  2. Nadaj klasie nazwę i Dodaj ją do projektu.

  3. Zastąp kod w pliku Stock.cs tym kodem:

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

    Dwie właściwości, które zostaną ustawione podczas tworzenia zasobów Symbol , to (na przykład MSFT dla firmy Microsoft) i Price . Inne właściwości zależą od tego, jak i po ustawieniu Price . Przy pierwszym ustawianiu Price wartość jest przekazywana do DayOpen . Po wybraniu tej opcji Price aplikacja oblicza Change PercentChange wartości właściwości i na podstawie różnicy między Price i DayOpen .

Tworzenie klas StockTickerHub i StockTicker

Użyjesz interfejsu API centrum sygnału do obsługi interakcji między serwerem a klientem. StockTickerHubKlasa, która pochodzi od klasy sygnalizującej, Hub będzie obsługiwać odbierające połączenia i wywołania metod od klientów. Należy również zachować dane giełdowe i uruchomić Timer obiekt. TimerObiekt będzie okresowo wyzwalać aktualizacje cen niezależnie od połączeń klientów. Nie można umieścić tych funkcji w Hub klasie, ponieważ centra są przejściowe. Aplikacja tworzy Hub wystąpienie klasy dla każdego zadania w centrum, takie jak połączenia i wywołania z klienta do serwera. Dlatego mechanizm, który przechowuje dane giełdowe, aktualizuje ceny i emituje aktualizacje cen, musi działać w oddzielnym klasie. Nazwa klasy zostanie nadana StockTicker .

Emitowanie z StockTicker

Należy tylko jedno wystąpienie StockTicker klasy do uruchomienia na serwerze, więc musisz skonfigurować odwołanie z każdego StockTickerHub wystąpienia do pojedynczego StockTicker wystąpienia. StockTickerKlasa musi emitować do klientów, ponieważ ma dane podstawowe i wyzwala aktualizacje, ale StockTicker nie jest Hub klasą. StockTickerKlasa musi uzyskać odwołanie do obiektu kontekstu połączenia centrum sygnału. Następnie może użyć obiektu kontekstu połączenia sygnalizującego do emisji do klientów.

Utwórz StockTickerHub.cs

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > nowy element.

  2. W obszarze Dodaj nowy element — sygnalizującer. StockTickerwybierz pozycję zainstalowany > sygnał sieci Web wjęzyku Visual C#, > Web > SignalR a następnie wybierz pozycję Klasa centrum sygnałów (v2).

  3. Nadaj klasie nazwę StockTickerHub i Dodaj ją do projektu.

    Ten krok powoduje utworzenie pliku klasy StockTickerHub.cs . Jednocześnie dodaje zestaw plików skryptów i odwołań do zestawów, które obsługują program sygnalizujący do projektu.

  4. Zastąp kod w pliku StockTickerHub.cs tym kodem:

    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. Zapisz plik.

Aplikacja używa klasy Hub do definiowania metod, które klienci mogą wywoływać na serwerze. Definiowana jest jedna metoda: GetAllStocks() . Gdy klient początkowo nawiązuje połączenie z serwerem, wywoła tę metodę, aby uzyskać listę wszystkich zasobów z ich bieżącymi cenami. Metoda może być uruchomiona synchronicznie i zwracać, IEnumerable<Stock> ponieważ zwraca dane z pamięci.

Jeśli metoda musiała pobrać dane, wykonując czynności, które mogą być zależne, takie jak wyszukiwanie bazy danych lub wywołanie usługi sieci Web, należy określić Task<IEnumerable<Stock>> jako wartość zwracaną, aby włączyć asynchroniczne przetwarzanie. Aby uzyskać więcej informacji, zobacz Podręcznik interfejsu API centrów ASP.NETer — serwer — Kiedy należy wykonać asynchronicznie.

Ten HubName atrybut określa, w jaki sposób aplikacja będzie odwoływać się do centrum w kodzie JavaScript na kliencie. Nazwa domyślna na kliencie, jeśli ten atrybut nie jest używany, to camelCase wersja klasy, która w tym przypadku byłaby stockTickerHub .

Jak zobaczysz później podczas tworzenia StockTicker klasy, aplikacja utworzy pojedyncze wystąpienie tej klasy we właściwości statycznej Instance . To pojedyncze wystąpienie StockTicker jest w pamięci, niezależnie od tego, ile klientów nawiązuje połączenie lub Rozłącz. To wystąpienie jest używane przez GetAllStocks() metodę do zwracania bieżących informacji o zapasach.

Utwórz StockTicker.cs

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > klasę.

  2. Nadaj klasie nazwę StockTicker i Dodaj ją do projektu.

  3. Zastąp kod w pliku StockTicker.cs tym kodem:

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

Ponieważ wszystkie wątki będą działać w tym samym wystąpieniu kodu StockTicker, Klasa StockTicker musi być bezpieczna wątkowo.

Sprawdzanie kodu serwera

Jeśli sprawdzisz kod serwera, pomoże Ci zrozumieć, jak działa aplikacja.

Przechowywanie pojedynczego wystąpienia w polu statycznym

Kod inicjuje _instance pole statyczne, które wykonuje kopię Instance właściwości z wystąpieniem klasy. Ponieważ Konstruktor jest prywatny, jest to jedyne wystąpienie klasy, które może utworzyć aplikacja. Aplikacja używa inicjowania z opóźnieniem dla _instance pola. Nie jest ze względu na wydajność. Należy upewnić się, że tworzenie wystąpienia jest bezpieczne wątkowo.

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

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

Za każdym razem, gdy klient nawiązuje połączenie z serwerem, nowe wystąpienie klasy StockTickerHub uruchomione w osobnym wątku Pobiera pojedyncze wystąpienie StockTicker z StockTicker.Instance właściwości statycznej, jak pokazano wcześniej w StockTickerHub klasie.

Przechowywanie danych giełdowych w ConcurrentDictionary

Konstruktor inicjuje _stocks kolekcję z niektórymi przykładowymi danymi zapasowymi i GetAllStocks zwraca zasoby. Jak widać wcześniej, ta kolekcja zasobów jest zwracana przez StockTickerHub.GetAllStocks , czyli Metoda serwera w Hub klasie, do której klienci mogą wywoływać.

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

Kolekcja magazynów jest definiowana jako typ ConcurrentDictionary dla bezpieczeństwa wątków. Alternatywnie, można użyć obiektu dictionary i jawnie zablokować słownik po wprowadzeniu w nim zmian.

W przypadku tej przykładowej aplikacji można przechowywać dane aplikacji w pamięci i utracić dane, gdy aplikacja zostanie wypisana z StockTicker wystąpienia. W rzeczywistej aplikacji można korzystać z magazynu danych zaplecza, takiego jak baza danych.

Okresowe aktualizowanie cen akcji

Konstruktor uruchamia Timer obiekt, który okresowo wywołuje metody, które codziennie aktualizują ceny giełdowe.

_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 wywołania UpdateStockPrices , które są przekazywane do wartości null w parametrze stanu. Przed aktualizacją cen aplikacja wykonuje blokadę _updateStockPricesLock obiektu. Kod sprawdza, czy inny wątek już aktualizuje ceny, a następnie wywołuje TryUpdateStockPrice wszystkie zasoby na liście. TryUpdateStockPriceMetoda decyduje o tym, czy należy zmienić cenę giełdową, i ile jej zmienić. Jeśli cena giełdowa ulegnie zmianie, aplikacja wywoła BroadcastStockPrice się w celu emisji zmiany ceny zapasów do wszystkich połączonych klientów.

_updatingStockPricesFlaga oznaczona jako nietrwała , aby upewnić się, że jest bezpieczna wątkowo.

private volatile bool _updatingStockPrices = false;

W rzeczywistej aplikacji TryUpdateStockPrice Metoda wywoła usługę sieci Web w celu wyszukania ceny. W tym kodzie aplikacja używa generatora liczb losowych, aby wprowadzać zmiany losowo.

Pobieranie kontekstu sygnalizującego, aby Klasa StockTicker mogła emitować się do klientów

Ponieważ zmiany cen pochodzą z tego miejsca w StockTicker obiekcie, jest to obiekt, który musi wywołać updateStockPrice metodę na wszystkich połączonych klientach. W Hub klasie jest interfejs API służący do wywoływania metod klienta, ale StockTicker nie pochodzi od Hub klasy i nie zawiera odwołania do żadnego Hub obiektu. Aby emitować do podłączonych klientów, StockTicker Klasa musi uzyskać wystąpienie kontekstu sygnalizującego dla StockTickerHub klasy i używać go do wywoływania metod na klientach.

Kod pobiera odwołanie do kontekstu sygnalizującego, gdy tworzy wystąpienie klasy pojedynczej, przekazuje odwołanie do konstruktora, a Konstruktor umieszcza go we Clients właściwości.

Istnieją dwa powody, dla których chcesz uzyskać kontekst tylko raz: uzyskanie kontekstu jest kosztownym zadaniem i wprowadzenie go, aby zachować zaplanowaną kolejność komunikatów wysyłanych do klientów.

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

Pobranie Clients właściwości kontekstu i umieszczenie jej we StockTickerClient Właściwości pozwala napisać kod do wywołania metod klienta, które wyglądają tak samo jak w Hub klasie. Na przykład, aby emitować do wszystkich klientów, które można napisać Clients.All.updateStockPrice(stock) .

updateStockPriceWywoływana metoda BroadcastStockPrice nie istnieje jeszcze. Dodasz go później podczas pisania kodu, który jest uruchamiany na kliencie. Tutaj można się odwołać updateStockPrice Clients.All , ponieważ jest to wartość dynamiczna, co oznacza, że aplikacja będzie szacować wyrażenie w czasie wykonywania. Gdy to wywołanie metody jest wykonywane, sygnalizujący wyśle do klienta nazwę metody i wartość parametru, a jeśli klient ma metodę o nazwie updateStockPrice , aplikacja wywoła tę metodę i przekaże do niej wartość parametru.

Clients.All oznacza wysyłanie do wszystkich klientów. Sygnalizujący oferuje inne opcje umożliwiające określenie klientów lub grup klientów, do których mają być wysyłane. Aby uzyskać więcej informacji, zobacz HubConnectionContext.

Rejestrowanie trasy sygnalizującej

Serwer musi wiedzieć, który adres URL przechwycić i bezpośrednio do sygnalizującego. Aby to zrobić, Dodaj klasę uruchomieniową OWIN:

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > nowy element.

  2. W obszarze Dodaj nowy element — sygnalizujący. StockTicker wybierz pozycję zainstalowana > Visual C# > Witryna internetowa Visual C#, a następnie wybierz pozycję Owin klasy startowej.

  3. Nazwij klasę uruchamiania i wybierz przycisk OK.

  4. Zastąp domyślny kod w pliku Startup.cs tym kodem:

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

Konfiguracja kodu serwera została zakończona. W następnej sekcji zostanie skonfigurowany klient.

Konfigurowanie kodu klienta

W tej sekcji skonfigurujesz kod, który jest uruchamiany na kliencie programu.

Tworzenie strony HTML i pliku JavaScript

Na stronie HTML zostaną wyświetlone dane, a plik JavaScript będzie organizować dane.

Utwórz StockTicker.html

Najpierw należy dodać klienta HTML.

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > stronę HTML.

  2. Nazwij plik StockTicker i wybierz przycisk OK.

  3. Zastąp domyślny kod w pliku StockTicker.html tym kodem:

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

    KOD HTML tworzy tabelę z pięcioma kolumnami, wierszem nagłówka i wierszem danych z pojedynczą komórką obejmującą wszystkie pięć kolumn. W wierszu danych zostanie wyświetlona wartość "ładowanie..." chwilę po uruchomieniu aplikacji. Kod JavaScript spowoduje usunięcie tego wiersza i dodanie go do swoich wierszy z danymi podstawowymi pobranymi z serwera.

    Tagi skryptu określają:

    • Plik skryptu jQuery.

    • Podstawowy plik skryptu sygnalizującego.

    • Plik skryptu dla serwerów proxy sygnałów.

    • Plik skryptu StockTicker, który utworzysz później.

    Aplikacja dynamicznie generuje plik skryptu dla serwerów proxy sygnalizującego. Określa adres URL "/SignalR/Hubs" i definiuje metody proxy dla metod w klasie centrów, w tym przypadku dla StockTickerHub.GetAllStocks . Jeśli wolisz, możesz wygenerować ten plik JavaScript ręcznie przy użyciu narzędzi sygnalizujących. Nie zapomnij wyłączyć tworzenia pliku dynamicznego w MapHubs wywołaniu metody.

  4. W Eksplorator rozwiązańrozwiń węzeł skrypty.

    Biblioteki skryptów dla jQuery i sygnalizujących są widoczne w projekcie.

    Important

    Menedżer pakietów zainstaluje nowszą wersję skryptów sygnalizujących.

  5. Zaktualizuj odwołania do skryptu w bloku kodu, aby odpowiadały wersji plików skryptów w projekcie.

  6. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy pozycję StockTicker.html, a następnie wybierz pozycję Ustaw jako stronę startową.

Utwórz StockTicker.js

Teraz Utwórz plik JavaScript.

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj > plik JavaScript.

  2. Nazwij plik StockTicker i wybierz przycisk OK.

  3. Dodaj następujący kod do pliku 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);
    
    });
    

Sprawdzanie kodu klienta

Po sprawdzeniu kodu klienta pomoże Ci dowiedzieć się, jak kod klienta współdziała z kodem serwera, aby umożliwić działanie aplikacji.

Uruchamianie połączenia

$.connection odwołuje się do serwerów proxy sygnalizujących. Kod pobiera odwołanie do serwera proxy dla StockTickerHub klasy i umieszcza je w ticker zmiennej. Nazwa serwera proxy jest nazwą ustawioną przez HubName atrybut:

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

Po zdefiniowaniu wszystkich zmiennych i funkcji ostatni wiersz kodu w pliku inicjuje połączenie sygnalizujące przez wywołanie funkcji sygnalizującej start . startFunkcja wykonuje asynchroniczne i zwraca odroczony obiekt jQuery. Można wywołać funkcję gotowe, aby określić funkcję do wywołania, gdy aplikacja zakończy akcję asynchroniczną.

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

Pobieranie wszystkich zasobów

initFunkcja wywołuje getAllStocks funkcję na serwerze i używa informacji zwracanych przez serwer w celu zaktualizowania tabeli giełdowej. Należy zauważyć, że domyślnie należy używać camelCasing na kliencie, nawet jeśli nazwa metody jest w języku Pascal-wielkość liter na serwerze. Reguła camelCasing dotyczy tylko metod, a nie obiektów. Na przykład odwołujesz się do stock.Symbol i stock.Price , nie stock.symbol lub 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();
}

W init metodzie aplikacja tworzy kod HTML dla wiersza tabeli dla każdego obiektu giełdowego otrzymanego z serwera, wywołując formatStock do właściwości formatowania stock obiektu, a następnie wywołując supplant do zastępowania symboli zastępczych w rowTemplate zmiennej za pomocą stock wartości właściwości obiektu. Otrzymany kod HTML jest następnie dołączany do tabeli giełdowej.

Note

Wywoływany init przez przekazanie go jako callback funkcji, która jest wykonywana po start zakończeniu funkcji asynchronicznej. Jeśli wywołano init jako oddzielną instrukcję JavaScript po wywołaniu start , funkcja nie powiedzie się, ponieważ zostanie uruchomiona natychmiast bez oczekiwania na zakończenie tworzenia połączenia przez funkcję startową. W takim przypadku init Funkcja spróbuje wywołać getAllStocks funkcję, zanim aplikacja nawiąże połączenie z serwerem.

Pobieranie zaktualizowanych cen giełdowych

Gdy serwer zmieni cenę giełdową, wywołuje updateStockPrice na podłączonych klientach. Aplikacja dodaje funkcję do właściwości client stockTicker serwera proxy, aby była dostępna dla wywołań z serwera.

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

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

updateStockPriceFunkcja formatuje obiekt giełdowy otrzymany z serwera w wierszu tabeli w taki sam sposób jak w init funkcji. Zamiast dołączyć wiersz do tabeli, znajduje bieżący wiersz zapasu w tabeli i zamienia ten wiersz na nowy.

Testowanie aplikacji

Możesz przetestować aplikację, aby upewnić się, że działa. Zobaczysz wszystkie okna przeglądarki wyświetlają dynamiczną tabelę giełdową z wahaniami cen giełdowych.

  1. Na pasku narzędzi Włącz debugowanie skryptów , a następnie wybierz przycisk Odtwórz, aby uruchomić aplikację w trybie debugowania.

    Zrzut ekranu przedstawiający Włączanie trybu debugowania przez użytkownika i wybieranie opcji Odtwórz.

    Zostanie otwarte okno przeglądarki z wyświetlaniem tabeli giełdowej na żywo. W tabeli giełdowej początkowo jest wyświetlana wartość "ładowanie..." Po krótkim czasie aplikacja wyświetli początkowe dane giełdowe, a następnie ceny giełdowe zaczynają się zmienić.

  2. Skopiuj adres URL z przeglądarki, Otwórz dwie inne przeglądarki i wklej adresy URL do pasków adresów.

    Początkowy wyświetlacz giełdowy jest taki sam jak pierwsza przeglądarka i zmiany są wykonywane jednocześnie.

  3. Zamknij wszystkie przeglądarki, Otwórz nową przeglądarkę i przejdź do tego samego adresu URL.

    Obiekt StockTicker singleton nadal działa na serwerze. W tabeli magazynu na żywo widać, że zasoby nadal się zmieniają. Nie widzisz tabeli początkowej ze zmianami o wartości zero.

  4. Zamknij okno przeglądarki.

Włącz rejestrowanie

Sygnalizujący ma wbudowaną funkcję rejestrowania, którą można włączyć na kliencie, aby pomóc w rozwiązywaniu problemów. W tej części należy włączyć rejestrowanie i zobaczyć przykłady pokazujące, w jaki sposób dzienniki poinformują o następujących metodach transportu:

Dla dowolnego połączenia sygnalizujący wybiera najlepszą metodę transportu, którą obsługuje serwer i klient.

  1. Otwórz StockTicker.js.

  2. Dodaj ten wyróżniony wiersz kodu, aby włączyć rejestrowanie bezpośrednio przed kodem, który inicjuje połączenie na końcu pliku:

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  3. Naciśnij klawisz F5 , aby uruchomić projekt.

  4. Otwórz okno narzędzia deweloperskie w przeglądarce i wybierz konsolę, aby wyświetlić dzienniki. Może być konieczne odświeżenie strony, aby wyświetlić dzienniki sygnalizujące transportowanie metody transportu dla nowego połączenia.

    • Jeśli używasz programu Internet Explorer 10 w systemie Windows 8 (IIS 8), Metoda transportowa to WebSockets.

    • Jeśli używasz programu Internet Explorer 10 w systemie Windows 7 (IIS 7,5), Metoda transportu jest iframe.

    • Jeśli korzystasz z programu Firefox 19 w systemie Windows 8 (IIS 8), Metoda transportu jest funkcją WebSockets.

      Tip

      W programie Firefox zainstaluj dodatek Firebug, aby uzyskać okno konsoli.

    • W przypadku korzystania z programu Firefox 19 w systemie Windows 7 (IIS 7,5) Metoda transportu to zdarzenia wysłane przez serwer .

Instalowanie przykładu StockTicker

Microsoft. ASPNET. Signal. przykład instaluje aplikację StockTicker. Pakiet NuGet zawiera więcej funkcji niż wersja uproszczona utworzona od podstaw. W tej części samouczka zainstalujesz pakiet NuGet i zapoznajesz się z nowymi funkcjami oraz kodem, który je implementuje.

Important

Jeśli pakiet zostanie zainstalowany bez wykonywania wcześniejszych kroków tego samouczka, należy dodać do projektu klasę uruchomieniową OWIN. Ten krok zawiera opis tego readme.txt pliku dla pakietu NuGet.

Zainstaluj program sygnalizujący. przykładowy pakiet NuGet

  1. W Eksplorator rozwiązańkliknij prawym przyciskiem myszy projekt i wybierz polecenie Zarządzaj pakietami NuGet.

  2. W Menedżerze pakietów NuGet: signaler. StockTicker, wybierz pozycję Przeglądaj.

  3. W obszarze Źródło pakietuwybierz pozycję NuGet.org.

  4. Wprowadź sygnalizowanie. przykład w polu wyszukiwania i wybierz pozycję Microsoft. ASPNET. Signal. sample > Install.

  5. W Eksplorator rozwiązańrozwiń folder sygnalizujący. sample .

    Instalowanie programu sygnalizującego. przykładowy pakiet utworzył folder i jego zawartość.

  6. W oknie sygnalizujący. przykład kliknij prawym przyciskiem myszy StockTicker.html, a następnie wybierz polecenie Ustaw jako stronę startową.

    Note

    Instalowanie programu sygnalizującego. przykładowy pakiet NuGet może zmienić wersję platformy jQuery, która znajduje się w folderze skryptów . Nowy plik StockTicker.html instalowany przez pakiet w sygnalizujące. przykładowy folder będzie synchronizowany z wersją jQuery, którą instaluje pakiet, ale jeśli chcesz ponownie uruchomić oryginalny plik StockTicker.html , może być konieczne zaktualizowanie odwołania jQuery w tagu skryptu.

Uruchamianie aplikacji

Tabela, która została zaprojektowana w pierwszej aplikacji, miała przydatne funkcje. W przypadku pełnego spisu aplikacji są widoczne nowe funkcje: okno przewijania w poziomie, które pokazuje dane giełdowe i zasoby, które zmieniają kolor w miarę wzrostu i spadku.

  1. Naciśnij klawisz F5 , aby uruchomić aplikację.

    Po uruchomieniu aplikacji po raz pierwszy "rynek" jest "zamknięty" i zobaczysz tabelę statyczną i okno znaczników, które nie jest przewijane.

  2. Wybierz pozycję Otwórz rynek.

    Zrzut ekranu dynamicznego znacznika.

    • Skrzynka giełdowa na żywo zostanie przesunięta w poziomie, a serwer rozpocznie okresowe emitowanie zmian cen giełdowych.

    • Za każdym razem, gdy zmieni się cena giełdowa, aplikacja aktualizuje zarówno dynamiczną , jak i giełdę na żywo.

    • Gdy zmiana ceny zapasów jest dodatnia, aplikacja pokazuje magazyn z zielonym tłem.

    • Gdy zmiana jest ujemna, aplikacja pokazuje zapas z czerwonym tłem.

  3. Wybierz pozycję Zamknij rynek.

    • Aktualizacja tabeli zatrzymuje się.

    • Znacznik przestaje przewijać.

  4. Wybierz pozycję Resetuj.

    • Wszystkie dane giełdowe są resetowane.

    • Aplikacja przywraca stan początkowy przed rozpoczęciem zmian cen.

  5. Skopiuj adres URL z przeglądarki, Otwórz dwie inne przeglądarki i wklej adresy URL do pasków adresów.

  6. Te same dane są automatycznie aktualizowane w każdej przeglądarce.

  7. Po wybraniu dowolnej z tych kontrolek wszystkie przeglądarki reagują w ten sam sposób w tym samym czasie.

Wyświetlanie grafu giełdowego na żywo

Wyświetlacz giełdowy na żywo jest nieuporządkowaną listą w <div> elemencie sformatowanym w jednym wierszu według stylów CSS. Aplikacja inicjuje i aktualizuje znaczniki w taki sam sposób jak tabela: przez zastąpienie symboli zastępczych w <li> ciągu szablonu i dynamiczne dodanie <li> elementów do <ul> elementu. Aplikacja obejmuje przewijanie przy użyciu funkcji jQuery w animate celu zaróżnienia lewego marginesu listy nieuporządkowanej w obrębie <div> .

Sygnalizujący. przykład StockTicker.html

Kod HTML znacznika giełdowego:

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

Sygnalizujący. przykład StockTicker. CSS

Kod CSS znacznika giełdowego:

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

Signaler. przykład SignalR.StockTicker.js

Kod jQuery, który umożliwia przewinięcie:

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

Dodatkowe metody na serwerze, z którym klient może wywoływać

Aby dodać elastyczność do aplikacji, istnieją dodatkowe metody, które może wywoływać aplikacja.

Sygnalizujący. przykład StockTickerHub.cs

StockTickerHubKlasa definiuje cztery dodatkowe metody, które klient może wywołać:

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

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

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

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

Aplikacja wywołuje OpenMarket , CloseMarket i Reset w odpowiedzi na przyciski w górnej części strony. Pokazują one wzorzec jednego klienta wyzwalającego zmianę stanu natychmiast propagowany do wszystkich klientów. Każda z tych metod wywołuje metodę w StockTicker klasie, która powoduje zmianę stanu rynku, a następnie emituje nowy stan.

Sygnalizujący. przykład StockTicker.cs

W StockTicker klasie aplikacja zachowuje stan rynku z MarketState właściwością zwracającą MarketState wartość wyliczenia:

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

public enum MarketState
{
    Closed,
    Open
}

Każda z metod, które zmieniają stan rynku, to w bloku blokady, ponieważ StockTicker Klasa musi być bezpieczna wątkowo:

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

Aby upewnić się, że ten kod jest bezpieczny dla wątków, _marketState pole, które wykonuje kopię zapasową MarketState wskazanej właściwości volatile :

private volatile MarketState _marketState;

BroadcastMarketStateChangeMetody i BroadcastMarketReset są podobne do metody BroadcastStockPrice, która została już wykorzystana, z tą różnicą, że wywołują różne metody zdefiniowane na kliencie:

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

Dodatkowe funkcje na kliencie, które może wywoływać serwer

updateStockPriceFunkcja obsługuje teraz zarówno tabelę, jak i wyświetlanie znaczników, i używa jQuery.Color do wypróbowania koloru czerwonego i zielonego.

Nowe funkcje w SignalR.StockTicker.js włączać i wyłączać przyciski na podstawie stanu rynku. Zatrzymają one również lub uruchamiają przewijanie w poziomie giełdy na żywo . Ponieważ wiele funkcji jest dodawanych do ticker.client , aplikacja używa funkcji rozszerzającej jQuery , aby je dodać.

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

Dodatkowa konfiguracja klienta po ustanowieniu połączenia

Po nawiązaniu połączenia klient ma kilka dodatkowych czynności do wykonania:

  • Sprawdź, czy rynek jest otwarty lub zamknięty, aby wywołać odpowiednie marketOpened lub marketClosed funkcję.

  • Dołącz wywołania metody serwera do przycisków.

$.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 serwera nie są połączone z przyciskami do momentu, gdy aplikacja nawiąże połączenie. Kod nie może wywoływać metod serwera przed ich udostępnieniem.

Zasoby dodatkowe

W tym samouczku przedstawiono sposób programowania aplikacji sygnalizującej, która emituje komunikaty z serwera do wszystkich połączonych klientów. Teraz można emitować wiadomości okresowo i w odpowiedzi na powiadomienia z dowolnego klienta. Można użyć koncepcji wielowątkowego wystąpienia pojedynczego, aby zachować stan serwera w scenariuszach gier online obejmujących wiele graczy. Aby zapoznać się z przykładem, zapoznaj się z grą korzystającą z usługi sygnalizującej.

Aby zapoznać się z samouczkami pokazującymi scenariusze komunikacji równorzędnej, zobacz wprowadzenie z sygnalizacją i aktualizacją w czasie rzeczywistym za pomocą usługi sygnalizującego.

Aby uzyskać więcej informacji o sygnalizacji, zobacz następujące zasoby:

Następne kroki

W tym samouczku zostały wykonane następujące czynności:

  • Utworzono projekt
  • Konfigurowanie kodu serwera
  • Zbadano kod serwera
  • Konfigurowanie kodu klienta
  • Zbadano kod klienta
  • Testowanie aplikacji
  • Włączone rejestrowanie

Przejdź do następnego artykułu, aby dowiedzieć się, jak utworzyć aplikację sieci Web w czasie rzeczywistym korzystającą z ASP.NET sygnalizującego 2.