Freigeben über


Tutorial: Serverübertragung mit SignalR 2

Warnung

Diese Dokumentation gilt nicht für die neueste Version von SignalR. Sehen Sie sich ASP.NET Core SignalR an.

In diesem Tutorial wird gezeigt, wie Sie eine Webanwendung erstellen, die ASP.NET SignalR 2 verwendet, um Serverübertragungsfunktionen bereitzustellen. Serverübertragung bedeutet, dass der Server die an Clients gesendete Kommunikation startet.

Die Anwendung, die Sie in diesem Tutorial erstellen, simuliert einen Aktienticker, ein typisches Szenario für Serverübertragungsfunktionen. In regelmäßigen Abständen aktualisiert der Server die Aktienkurse nach dem Zufallsprinzip und sendet die Updates an alle verbundenen Clients. Im Browser ändern sich die Zahlen und Symbole in den Spalten Ändern und % dynamisch als Reaktion auf Benachrichtigungen vom Server. Wenn Sie zusätzliche Browser für dieselbe URL öffnen, werden alle gleichzeitig die gleichen Daten und die gleichen Änderungen an den Daten angezeigt.

Screenshot: Mehrere Webbrowser zeigen die gleichen aktualisierten Daten gleichzeitig an.

In diesem Tutorial führen Sie Folgendes durch:

  • Erstellen des Projekts
  • Einrichten des Servercodes
  • Untersuchen des Servercodes
  • Einrichten des Clientcodes
  • Untersuchen des Clientcodes
  • Testen der Anwendung
  • Aktivieren der Protokollierung

Wichtig

Wenn Sie die Schritte zum Erstellen der Anwendung nicht durcharbeiten möchten, können Sie das Paket SignalR.Sample in einem neuen Projekt für leere ASP.NET-Webanwendung installieren. Wenn Sie das NuGet-Paket installieren, ohne die Schritte in diesem Tutorial auszuführen, müssen Sie die Anweisungen in der dateireadme.txt befolgen. Zum Ausführen des Pakets müssen Sie eine OWIN-Startklasse hinzufügen, die die ConfigureSignalR -Methode im installierten Paket aufruft. Sie erhalten eine Fehlermeldung, wenn Sie die OWIN-Startklasse nicht hinzufügen. Weitere Informationen finden Sie im Abschnitt Installieren des StockTicker-Beispiels in diesem Artikel.

Voraussetzungen

Erstellen des Projekts

In diesem Abschnitt wird gezeigt, wie Sie mit Visual Studio 2017 eine leere ASP.NET Webanwendung erstellen.

  1. Erstellen Sie in Visual Studio eine ASP.NET Webanwendung.

    Screenshot: Erstellen einer ASP.NET-Webanwendung

  2. Lassen Sie im Fenster Neue ASP.NET Webanwendung – SignalR.StockTickerleer ausgewählt, und wählen Sie OK aus.

Einrichten des Servercodes

In diesem Abschnitt richten Sie den Code ein, der auf dem Server ausgeführt wird.

Erstellen der Stock-Klasse

Sie erstellen zunächst die Stock-Modellklasse , die Sie zum Speichern und Übertragen von Informationen zu einem Bestand verwenden.

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieKlassehinzufügen> aus.

  2. Nennen Sie die Klasse Stock , und fügen Sie sie dem Projekt hinzu.

  3. Ersetzen Sie den Code in der Datei Stock.cs durch diesen Code:

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

    Die beiden Eigenschaften, die Sie beim Erstellen von Aktien festlegen, sind Symbol (z. B. MSFT für Microsoft) und Price. Die anderen Eigenschaften hängen davon ab, wie und wann Sie festlegen Price. Wenn Sie zum ersten Mal festlegen Price, wird der Wert an DayOpenweitergegeben. Danach berechnet die App beim Festlegen Pricedie Change Eigenschaftenwerte und PercentChange basierend auf der Differenz zwischen Price und DayOpen.

Erstellen der StockTickerHub- und StockTicker-Klassen

Sie verwenden die SignalR Hub-API, um die Server-zu-Client-Interaktion zu verarbeiten. Eine StockTickerHub Klasse, die von der SignalR-Klasse Hub abgeleitet ist, verarbeitet den Empfang von Verbindungen und Methodenaufrufen von Clients. Außerdem müssen Sie Bestandsdaten verwalten und ein Timer Objekt ausführen. Das Timer -Objekt löst regelmäßig Preisaktualisierungen aus, unabhängig von Clientverbindungen. Sie können diese Funktionen nicht in eine Hub Klasse einfügen, da Hubs vorübergehend sind. Die App erstellt eine Hub Klasse instance für jede Aufgabe auf dem Hub, z. B. Verbindungen und Aufrufe vom Client an den Server. Daher muss der Mechanismus, der Aktiendaten speichert, Preise aktualisiert und die Preisupdates überträgt, in einer separaten Klasse ausgeführt werden. Sie nennen die Klasse StockTicker.

Übertragung aus StockTicker

Sie möchten nur eine instance der StockTicker Klasse auf dem Server ausführen. Daher müssen Sie einen Verweis von jedem StockTickerHub instance auf die Singleton-instance StockTicker einrichten. Die StockTicker Klasse muss an Clients übertragen werden, da sie die Bestandsdaten enthält und Updates auslöst, aber StockTicker keine Hub Klasse ist. Die StockTicker -Klasse muss einen Verweis auf das SignalR Hub-Verbindungskontextobjekt abrufen. Anschließend kann das SignalR-Verbindungskontextobjekt zum Senden an Clients verwendet werden.

Erstellen von StockTickerHub.cs

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieNeues Elementhinzufügen> aus.

  2. Wählen Sie unter Neues Element hinzufügen – SignalR.StockTickerdie Option Installiertes>Visual C#>Web>SignalR und dann SignalR Hub-Klasse (v2) aus.

  3. Nennen Sie die Klasse StockTickerHub , und fügen Sie sie dem Projekt hinzu.

    In diesem Schritt wird die Klassendatei StockTickerHub.cs erstellt. Gleichzeitig werden dem Projekt skriptdateien und Assemblyverweise hinzugefügt, die SignalR unterstützen.

  4. Ersetzen Sie den Code in der Datei StockTickerHub.cs durch diesen Code:

    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. Speichern Sie die Datei .

Die App verwendet die Hub-Klasse , um Methoden zu definieren, die clients auf dem Server aufrufen können. Sie definieren eine Methode: GetAllStocks(). Wenn ein Client zunächst eine Verbindung mit dem Server herstellt, ruft er diese Methode auf, um eine Liste aller Bestände mit ihren aktuellen Preisen abzurufen. Die Methode kann synchron ausgeführt und zurückgegeben IEnumerable<Stock> werden, da sie Daten aus dem Arbeitsspeicher zurückgibt.

Wenn die Methode die Daten abrufen müsste, indem Sie etwas ausführen, das warten würde, z. B. eine Datenbanksuche oder einen Webdienstaufruf, würden Sie als Rückgabewert angeben Task<IEnumerable<Stock>> , um die asynchrone Verarbeitung zu aktivieren. Weitere Informationen finden Sie unter ASP.NET SignalR Hubs API Guide – Server – Wann asynchron ausgeführt werden soll.

Das HubName Attribut gibt an, wie die App im JavaScript-Code auf dem Client auf den Hub verweist. Der Standardname auf dem Client, wenn Sie dieses Attribut nicht verwenden, ist eine camelCase-Version des Klassennamens, der in diesem Fall lautet stockTickerHub.

Wie Sie später sehen werden, wenn Sie die StockTicker -Klasse erstellen, erstellt die App einen Singleton-instance dieser Klasse in ihrer statischen Instance Eigenschaft. Diese Singleton-instance von StockTicker befindet sich im Arbeitsspeicher, unabhängig davon, wie viele Clients eine Verbindung herstellen oder trennen. Dies instance ist das, was die GetAllStocks() Methode verwendet, um aktuelle Bestandsinformationen zurückzugeben.

Erstellen von StockTicker.cs

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieKlassehinzufügen> aus.

  2. Nennen Sie die Klasse StockTicker , und fügen Sie sie dem Projekt hinzu.

  3. Ersetzen Sie den Code in der Datei StockTicker.cs durch diesen Code:

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

Da alle Threads denselben instance von StockTicker-Code ausführen, muss die StockTicker-Klasse threadsicher sein.

Untersuchen des Servercodes

Wenn Sie den Servercode untersuchen, können Sie besser verstehen, wie die App funktioniert.

Speichern des Singleton-instance in einem statischen Feld

Der Code initialisiert das statische _instance Feld, das die Instance -Eigenschaft mit einem instance der -Klasse zurückgibt. Da der Konstruktor privat ist, ist er die einzige instance der Klasse, die die App erstellen kann. Die App verwendet die Lazy-Initialisierung für das _instance Feld. Dies ist nicht aus Leistungsgründen. Dies ist, um sicherzustellen, dass die instance Erstellung threadsicher ist.

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

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

Jedes Mal, wenn ein Client eine Verbindung mit dem Server herstellt, ruft eine neue instance der StockTickerHub-Klasse, die in einem separaten Thread ausgeführt wird, den StockTicker Singleton instance aus der StockTicker.Instance statischen Eigenschaft ab, wie Sie zuvor in der StockTickerHub Klasse gesehen haben.

Speichern von Bestandsdaten in einem ConcurrentDictionary

Der Konstruktor initialisiert die _stocks Auflistung mit einigen Beispieldaten und GetAllStocks gibt die Bestände zurück. Wie Sie bereits gesehen haben, wird diese Sammlung von zurückgegeben, StockTickerHub.GetAllStocksbei der es sich um eine Servermethode in der Klasse handelt, die Hub von Clients aufgerufen werden kann.

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

Die Bestandsauflistung ist für die Threadsicherheit als ConcurrentDictionary-Typ definiert. Alternativ können Sie ein Dictionary-Objekt verwenden und das Wörterbuch explizit sperren, wenn Sie Änderungen daran vornehmen.

Für diese Beispielanwendung ist es in Ordnung, Anwendungsdaten im Arbeitsspeicher zu speichern und die Daten zu verlieren, wenn die App die StockTicker instance entsorgt. In einer realen Anwendung würden Sie mit einem Back-End-Datenspeicher wie einer Datenbank arbeiten.

Regelmäßige Aktualisierung der Aktienkurse

Der Konstruktor startet ein Timer -Objekt, das regelmäßig Methoden aufruft, die Aktienkurse nach dem Zufallsprinzip aktualisieren.

_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 ruft auf UpdateStockPrices, wodurch null im state-Parameter übergeben wird. Vor der Aktualisierung der Preise nimmt die App eine Sperre für das _updateStockPricesLock -Objekt ein. Der Code überprüft, ob bereits ein anderer Thread die Preise aktualisiert, und ruft TryUpdateStockPrice dann für jede Aktie in der Liste auf. Die TryUpdateStockPrice -Methode entscheidet, ob der Aktienkurs geändert werden soll und in welchem Maße er geändert werden soll. Wenn sich der Aktienkurs ändert, ruft die App auf BroadcastStockPrice , um den Aktienkurs an alle verbundenen Clients zu übertragen.

Das _updatingStockPrices Flag, das als flüchtig gekennzeichnet ist, um sicherzustellen, dass es threadsicher ist.

private volatile bool _updatingStockPrices = false;

In einer echten Anwendung ruft die TryUpdateStockPrice -Methode einen Webdienst auf, um den Preis nachzuschlagen. In diesem Code verwendet die App einen Zufallszahlengenerator, um nach dem Zufallsprinzip Änderungen vorzunehmen.

Abrufen des SignalR-Kontexts, damit die StockTicker-Klasse an Clients übertragen werden kann

Da die Preisänderungen hier im StockTicker -Objekt entstehen, ist es das -Objekt, das eine updateStockPrice -Methode auf allen verbundenen Clients aufrufen muss. In einer Hub Klasse verfügen Sie über eine API zum Aufrufen von Clientmethoden, die jedoch StockTicker nicht von der Hub -Klasse abgeleitet ist und keinen Verweis auf ein Hub Objekt aufweist. Zum Übertragen an verbundene Clients muss die Klasse den StockTicker SignalR-Kontext instance für die -Klasse abrufen und zum Aufrufen von StockTickerHub Methoden auf Clients verwenden.

Der Code ruft einen Verweis auf den SignalR-Kontext ab, wenn er die Singleton-Klasse instance erstellt, diesen Verweis an den Konstruktor übergibt, und der Konstruktor fügt ihn in der Clients -Eigenschaft ein.

Es gibt zwei Gründe, warum Sie den Kontext nur einmal abrufen möchten: Das Abrufen des Kontexts ist eine kostspielige Aufgabe, und wenn Sie ihn einmal erhalten, wird sichergestellt, dass die App die beabsichtigte Reihenfolge der an die Clients gesendeten Nachrichten beibekommt.

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

Wenn Sie die Clients -Eigenschaft des Kontexts abrufen und in die StockTickerClient -Eigenschaft einfügen, können Sie Code schreiben, um Clientmethoden aufzurufen, die genauso aussehen wie in einer Hub Klasse. Für instance können Clients.All.updateStockPrice(stock)Sie schreiben, um an alle Clients zu übertragen.

Die updateStockPrice Methode, in der Sie aufrufen BroadcastStockPrice , ist noch nicht vorhanden. Sie fügen sie später hinzu, wenn Sie Code schreiben, der auf dem Client ausgeführt wird. Sie können hier darauf verweisen updateStockPrice , da Clients.All dynamisch ist, was bedeutet, dass die App den Ausdruck zur Laufzeit auswertet. Wenn dieser Methodenaufruf ausgeführt wird, sendet SignalR den Methodennamen und den Parameterwert an den Client, und wenn der Client über eine Methode namens updateStockPriceverfügt, ruft die App diese Methode auf und übergibt den Parameterwert an sie.

Clients.All bedeutet, an alle Clients zu senden. SignalR bietet Ihnen weitere Optionen, um anzugeben, an welche Clients oder Gruppen von Clients gesendet werden sollen. Weitere Informationen finden Sie unter HubConnectionContext.

Registrieren der SignalR-Route

Der Server muss wissen, welche URL abgefangen und an SignalR weiterzuleiten ist. Fügen Sie dazu eine OWIN-Startklasse hinzu:

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieNeues Elementhinzufügen> aus.

  2. Wählen Sie unter Neues Element hinzufügen – SignalR.StockTicker die Option VisualC#>Webinstalliert> aus, und wählen Sie dann OWIN-Startklasse aus.

  3. Nennen Sie die Klasse Startup, und wählen Sie OK aus.

  4. Ersetzen Sie den Standardcode in der Datei Startup.cs durch den folgenden Code:

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

Sie haben nun die Einrichtung des Servercodes abgeschlossen. Im nächsten Abschnitt richten Sie den Client ein.

Einrichten des Clientcodes

In diesem Abschnitt richten Sie den Code ein, der auf dem Client ausgeführt wird.

Erstellen der HTML-Seite und der JavaScript-Datei

Auf der HTML-Seite werden die Daten angezeigt, und die JavaScript-Datei organisiert die Daten.

Erstellen von StockTicker.html

Zuerst fügen Sie den HTML-Client hinzu.

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieHTML-Seitehinzufügen> aus.

  2. Nennen Sie die Datei StockTicker , und wählen Sie OK aus.

  3. Ersetzen Sie den Standardcode in der StockTicker.html-Datei durch diesen Code:

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

    Der HTML-Code erstellt eine Tabelle mit fünf Spalten, eine Kopfzeile und eine Datenzeile mit einer einzelnen Zelle, die sich über alle fünf Spalten erstreckt. In der Datenzeile wird "loading..." angezeigt. Moment, wenn die App gestartet wird. JavaScript-Code entfernt diese Zeile und fügt an ihrer Stelle Zeilen mit vom Server abgerufenen Bestandsdaten hinzu.

    Die Skripttags geben Folgendes an:

    • Die jQuery-Skriptdatei.

    • Die SignalR-Kernskriptdatei.

    • Die SignalR-Proxyskriptdatei.

    • Eine StockTicker-Skriptdatei, die Sie später erstellen.

    Die App generiert dynamisch die SignalR-Proxyskriptdatei. Sie gibt die URL "/signalr/hubs" an und definiert Proxymethoden für die Methoden in der Hub-Klasse, in diesem Fall für StockTickerHub.GetAllStocks. Wenn Sie möchten, können Sie diese JavaScript-Datei manuell mithilfe von SignalR Utilities generieren. Vergessen Sie nicht, die MapHubs dynamische Dateierstellung im Methodenaufruf zu deaktivieren.

  4. Erweitern Sie Projektmappen-ExplorerSkripts.

    Skriptbibliotheken für jQuery und SignalR sind im Projekt sichtbar.

    Wichtig

    Der Paket-Manager installiert eine höhere Version der SignalR-Skripts.

  5. Aktualisieren Sie die Skriptverweise im Codeblock so, dass sie den Versionen der Skriptdateien im Projekt entsprechen.

  6. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf StockTicker.html, und wählen Sie dann Als Startseite festlegen aus.

Erstellen von StockTicker.js

Erstellen Sie nun die JavaScript-Datei.

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieJavaScript-Dateihinzufügen> aus.

  2. Nennen Sie die Datei StockTicker , und wählen Sie OK aus.

  3. Fügen Sie den folgenden Code zur StockTicker.js-Datei hinzu:

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

Untersuchen des Clientcodes

Wenn Sie den Clientcode untersuchen, erfahren Sie, wie der Clientcode mit dem Servercode interagiert, damit die App funktioniert.

Starten der Verbindung

$.connection bezieht sich auf die SignalR-Proxys. Der Code ruft einen Verweis auf den Proxy für die StockTickerHub Klasse ab und fügt ihn in die ticker Variable ein. Der Proxyname ist der Name, der durch das HubName -Attribut festgelegt wurde:

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

Nachdem Sie alle Variablen und Funktionen definiert haben, initialisiert die letzte Codezeile in der Datei die SignalR-Verbindung, indem die SignalR-Funktion start aufgerufen wird. Die start Funktion wird asynchron ausgeführt und gibt ein jQuery Deferred-Objekt zurück. Sie können die funktion done aufrufen, um die Funktion anzugeben, die aufgerufen werden soll, wenn die App die asynchrone Aktion beendet.

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

Abrufen aller Aktien

Die init Funktion ruft die getAllStocks Funktion auf dem Server auf und verwendet die Vom Server zurückgegebenen Informationen, um die Lagertabelle zu aktualisieren. Beachten Sie, dass Sie standardmäßig camelCasing auf dem Client verwenden müssen, obwohl der Methodenname auf dem Server pascal-cased ist. Die camelCasing-Regel gilt nur für Methoden, nicht für Objekte. Sie verweisen z. B. auf stock.Symbol und stock.Price, nicht stock.symbol oder 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();
}

In der init -Methode erstellt die App HTML für eine Tabellenzeile für jedes vom Server empfangene Bestandsobjekt, indem sie die Formateigenschaften des stock Objekts aufruft formatStock und dann aufruftsupplant, um Platzhalter in der rowTemplate Variablen durch die stock Objekteigenschaftswerte zu ersetzen. Der resultierende HTML-Code wird dann an die Bestandstabelle angefügt.

Hinweis

Sie rufen auf init , indem Sie sie als Funktion callback übergeben, die nach Abschluss der asynchronen start Funktion ausgeführt wird. Wenn Sie nach dem Aufrufen startvon als separate JavaScript-Anweisung aufrufeninit, schlägt die Funktion fehl, da sie sofort ausgeführt wird, ohne darauf zu warten, dass die Startfunktion die Verbindung herstellt. In diesem Fall würde die init Funktion versuchen, die getAllStocks Funktion aufzurufen, bevor die App eine Serververbindung herstellt.

Abrufen aktualisierter Aktienkurse

Wenn der Server den Kurs einer Aktie ändert, ruft er die updateStockPrice auf verbundenen Clients auf. Die App fügt die Funktion der Clienteigenschaft des stockTicker Proxys hinzu, um sie für Aufrufe vom Server verfügbar zu machen.

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

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

Die updateStockPrice Funktion formatiert ein vom Server empfangenes Bestandsobjekt auf die gleiche Weise wie in der init -Funktion in eine Tabellenzeile. Anstatt die Zeile an die Tabelle anzufügen, sucht es die aktuelle Zeile des Lagerbestands in der Tabelle und ersetzt diese Zeile durch die neue Zeile.

Testen der Anwendung

Sie können die App testen, um sicherzustellen, dass sie funktioniert. In allen Browserfenstern wird die Live-Aktientabelle mit schwankenden Aktienkursen angezeigt.

  1. Aktivieren Sie auf der Symbolleiste Skriptdebuggen , und wählen Sie dann die Wiedergabeschaltfläche aus, um die App im Debugmodus auszuführen.

    Screenshot: Benutzer, der den Debugmodus aktiviert und wiedergaben auswählt.

    Ein Browserfenster wird geöffnet, in dem die Live Stock-Tabelle angezeigt wird. Die Lagertabelle zeigt zunächst das "Laden..." Nach kurzer Zeit zeigt die App dann die anfänglichen Aktiendaten an, und dann beginnen sich die Aktienkurse zu ändern.

  2. Kopieren Sie die URL aus dem Browser, öffnen Sie zwei andere Browser, und fügen Sie die URLs in die Adressleisten ein.

    Die anfängliche Bestandsanzeige ist identisch mit dem ersten Browser, und Änderungen erfolgen gleichzeitig.

  3. Schließen Sie alle Browser, öffnen Sie einen neuen Browser, und wechseln Sie zur gleichen URL.

    Das StockTicker-Singletonobjekt wurde weiterhin auf dem Server ausgeführt. Die Live Stock Table zeigt, dass sich die Aktien weiter verändert haben. Die Anfangstabelle mit null Änderungszahlen wird nicht angezeigt.

  4. Schließen Sie den Browser.

Aktivieren der Protokollierung

SignalR verfügt über eine integrierte Protokollierungsfunktion, die Sie auf dem Client aktivieren können, um die Problembehandlung zu unterstützen. In diesem Abschnitt aktivieren Sie die Protokollierung und sehen Sich Beispiele an, die zeigen, wie Protokolle Ihnen mitteilen, welche der folgenden Transportmethoden SignalR verwendet:

Für jede bestimmte Verbindung wählt SignalR die beste Transportmethode aus, die sowohl vom Server als auch vom Client unterstützt wird.

  1. Öffnen Sie StockTicker.js.

  2. Fügen Sie diese hervorgehobene Codezeile hinzu, um die Protokollierung unmittelbar vor dem Code zu aktivieren, der die Verbindung am Ende der Datei initialisiert:

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  3. Drücken Sie F5, um das Projekt auszuführen.

  4. Öffnen Sie das Entwicklertoolsfenster Ihres Browsers, und wählen Sie die Konsole aus, um die Protokolle anzuzeigen. Möglicherweise müssen Sie die Seite aktualisieren, um die Protokolle von SignalR anzuzeigen, die die Transportmethode für eine neue Verbindung verhandelt.

    • Wenn Sie Internet Explorer 10 unter Windows 8 (IIS 8) ausführen, lautet die Transportmethode WebSockets.

    • Wenn Sie Internet Explorer 10 unter Windows 7 (IIS 7.5) ausführen, lautet die Transportmethode iframe.

    • Wenn Sie Firefox 19 unter Windows 8 (IIS 8) ausführen, lautet die Transportmethode WebSockets.

      Tipp

      Installieren Sie in Firefox das Firebug-Add-In, um ein Konsolenfenster abzurufen.

    • Wenn Sie Firefox 19 unter Windows 7 (IIS 7.5) ausführen, handelt es sich bei der Transportmethode um vom Server gesendete Ereignisse.

Installieren des StockTicker-Beispiels

Microsoft.AspNet.SignalR.Sample installiert die StockTicker-Anwendung. Das NuGet-Paket enthält mehr Features als die vereinfachte Version, die Sie von Grund auf neu erstellt haben. In diesem Abschnitt des Tutorials installieren Sie das NuGet-Paket und überprüfen die neuen Features und den Code, der sie implementiert.

Wichtig

Wenn Sie das Paket installieren, ohne die vorherigen Schritte dieses Tutorials auszuführen, müssen Sie Ihrem Projekt eine OWIN-Startklasse hinzufügen. In dieser readme.txt Datei für das NuGet-Paket wird dieser Schritt erläutert.

Installieren des SignalR.Sample NuGet-Pakets

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie NuGet-Pakete verwalten aus.

  2. Wählen Sie im NuGet-Paket-Manager SignalR.StockTickerdie Option Durchsuchen aus.

  3. Wählen Sie unter Paketquelledie Option nuget.org aus.

  4. Geben Sie SignalR.Sample in das Suchfeld ein, und wählen Sie Microsoft.AspNet.SignalR.Sample>Install aus.

  5. Erweitern Sie in Projektmappen-Explorer den Ordner SignalR.Sample.

    Beim Installieren des Pakets SignalR.Sample wurden der Ordner und dessen Inhalt erstellt.

  6. Klicken Sie im Ordner SignalR.Sample mit der rechten Maustaste auf StockTicker.html, und wählen Sie dann Als Startseite festlegen aus.

    Hinweis

    Wenn Sie das NuGet-Paket SignalR.Sample installieren, ändert sich möglicherweise die Version von jQuery, die Sie in Ihrem Skriptordner haben. Die neue StockTicker.html-Datei , die das Paket im Ordner SignalR.Sample installiert, wird mit der jQuery-Version synchronisiert, die das Paket installiert. Wenn Sie ihre ursprüngliche StockTicker.html-Datei jedoch erneut ausführen möchten, müssen Sie möglicherweise zuerst den jQuery-Verweis im Skripttag aktualisieren.

Ausführen der Anwendung

Die Tabelle, die Sie in der ersten App gesehen haben, enthielt nützliche Features. Die vollständige Aktienticker-Anwendung zeigt neue Features: ein horizontal scrollendes Fenster, in dem die Aktiendaten und Aktien angezeigt werden, die ihre Farbe ändern, wenn sie steigen und fallen.

  1. Drücken Sie F5 , um die App auszuführen.

    Wenn Sie die App zum ersten Mal ausführen, wird der "Markt" geschlossen, und Es wird eine statische Tabelle und ein Tickerfenster angezeigt, in dem kein Bildlauf ausgeführt wird.

  2. Wählen Sie Open Market (Markt öffnen) aus.

    Screenshot des Livetickers.

    • Das Feld Live-Aktienticker beginnt horizontal zu scrollen, und der Server beginnt, aktienkursänderungen in regelmäßigen Abständen nach dem Zufallsprinzip zu übertragen.

    • Jedes Mal, wenn sich ein Aktienkurs ändert, aktualisiert die App sowohl die Live Stock Table als auch den Live Stock Ticker.

    • Wenn die Kursänderung einer Aktie positiv ist, zeigt die App die Aktie mit grünem Hintergrund an.

    • Wenn die Änderung negativ ist, zeigt die App die Aktie mit rotem Hintergrund an.

  3. Wählen Sie Markt schließen aus.

    • Die Tabellenupdates werden beendet.

    • Der Ticker beendet den Bildlauf.

  4. Klicken Sie auf Zurücksetzen.

    • Alle Bestandsdaten werden zurückgesetzt.

    • Die App stellt den Anfangszustand wieder her, bevor die Preisänderungen gestartet werden.

  5. Kopieren Sie die URL aus dem Browser, öffnen Sie zwei andere Browser, und fügen Sie die URLs in die Adressleisten ein.

  6. In jedem Browser werden die gleichen Daten dynamisch gleichzeitig aktualisiert.

  7. Wenn Sie eines der Steuerelemente auswählen, reagieren alle Browser gleichzeitig auf die gleiche Weise.

Live-Aktienticker-Anzeige

Die Live Stock Ticker-Anzeige ist eine ungeordnete Liste in einem <div> Element, das nach CSS-Formatvorlagen in eine einzelne Zeile formatiert ist. Die App initialisiert und aktualisiert den Ticker auf die gleiche Weise wie die Tabelle: durch Ersetzen von Platzhaltern in einer <li> Vorlagenzeichenfolge und dynamisches Hinzufügen der <li> Elemente zum <ul> Element. Die App umfasst das Scrollen mithilfe der jQuery-Funktion animate , um den Rand links der ungeordneten Liste innerhalb von <div>zu variieren.

SignalR.Sample StockTicker.html

Der HTML-Code des Aktientickers:

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

SignalR.Sample StockTicker.css

Der CSS-Code des Aktientickers:

#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.Sample SignalR.StockTicker.js

Der jQuery-Code, der den Bildlauf ermöglicht:

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

Zusätzliche Methoden auf dem Server, die der Client aufrufen kann

Um die App flexibler zu machen, gibt es zusätzliche Methoden, die die App aufrufen kann.

SignalR.Sample StockTickerHub.cs

Die StockTickerHub -Klasse definiert vier zusätzliche Methoden, die der Client aufrufen kann:

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

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

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

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

Die App ruft OpenMarket, CloseMarketund Reset als Reaktion auf die Schaltflächen oben auf der Seite auf. Sie veranschaulichen das Muster, dass ein Client eine Zustandsänderung auslöst, die sofort an alle Clients weitergegeben wird. Jede dieser Methoden ruft eine Methode in der StockTicker -Klasse auf, die die Änderung des Marktzustands verursacht, und sendet dann den neuen Zustand.

SignalR.Sample StockTicker.cs

In der StockTicker -Klasse verwaltet die App den Zustand des Marktes mit einer MarketState Eigenschaft, die einen MarketState Enumerationswert zurückgibt:

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

public enum MarketState
{
    Closed,
    Open
}

Jede der Methoden, die den Marktzustand ändern, tun dies innerhalb eines Sperrblocks, da die StockTicker Klasse threadsicher sein muss:

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

Um sicherzustellen, dass dieser Code threadsicher ist, verwenden Sie das _marketState Feld, das die MarketState angegebene Eigenschaft zurückgibt volatile:

private volatile MarketState _marketState;

Die BroadcastMarketStateChange Methoden und BroadcastMarketReset ähneln der BroadcastStockPrice-Methode, die Sie bereits gesehen haben, mit der Ausnahme, dass sie verschiedene Methoden aufrufen, die auf dem Client definiert sind:

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

Zusätzliche Funktionen auf dem Client, die der Server aufrufen kann

Die updateStockPrice Funktion verarbeitet jetzt sowohl die Tabelle als auch die Tickeranzeige und verwendet jQuery.Color , um rote und grüne Farben zu blinken.

Neue Funktionen in SignalR.StockTicker.js die Schaltflächen basierend auf dem Marktzustand aktivieren und deaktivieren. Außerdem wird der horizontale Bildlauf des Live-Aktientickers beendet oder gestartet. Da viele Funktionen hinzugefügt ticker.clientwerden, verwendet die App die Funktion jQuery extend , um sie hinzuzufügen.

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

Zusätzliche Clienteinrichtung nach dem Herstellen der Verbindung

Nachdem der Client die Verbindung hergestellt hat, hat er einige zusätzliche Aufgaben zu erledigen:

  • Finden Sie heraus, ob der Markt geöffnet oder geschlossen ist, um die entsprechende marketOpened Funktion oder marketClosed funktion aufzurufen.

  • Fügen Sie die Servermethodenaufrufe an die Schaltflächen an.

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

Die Servermethoden werden erst mit den Schaltflächen verbunden, nachdem die App die Verbindung hergestellt hat. Dies ist so, dass der Code die Servermethoden nicht aufrufen kann, bevor sie verfügbar sind.

Zusätzliche Ressourcen

In diesem Tutorial haben Sie gelernt, wie Sie eine SignalR-Anwendung programmieren, die Nachrichten vom Server an alle verbundenen Clients sendet. Jetzt können Sie Nachrichten regelmäßig und als Reaktion auf Benachrichtigungen von jedem Client übertragen. Sie können das Konzept der Multithread-Singleton-instance verwenden, um den Serverzustand in Szenarien mit Mehreren-Spieler-Onlinespielen beizubehalten. Ein Beispiel finden Sie unter ShootR-Spiel basierend auf SignalR.

Tutorials zu Peer-to-Peer-Kommunikationsszenarien finden Sie unter Erste Schritte mit SignalR und Echtzeitaktualisierung mit SignalR.

Weitere Informationen zu SignalR finden Sie in den folgenden Ressourcen:

Nächste Schritte

In diesem Tutorial führen Sie Folgendes durch:

  • Projekt erstellt
  • Einrichten des Servercodes
  • Untersuchte den Servercode
  • Einrichten des Clientcodes
  • Überprüfen des Clientcodes
  • Testen der Anwendung
  • Protokollierung aktiviert

Fahren Sie mit dem nächsten Artikel fort, um zu erfahren, wie Sie eine Echtzeit-Webanwendung erstellen, die ASP.NET SignalR 2 verwendet.