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.
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
- Visual Studio 2017 mit der Workload ASP.NET und Webentwicklung
Erstellen des Projekts
In diesem Abschnitt wird gezeigt, wie Sie mit Visual Studio 2017 eine leere ASP.NET Webanwendung erstellen.
Erstellen Sie in Visual Studio eine ASP.NET Webanwendung.
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.
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieKlassehinzufügen> aus.
Nennen Sie die Klasse Stock , und fügen Sie sie dem Projekt hinzu.
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) undPrice
. Die anderen Eigenschaften hängen davon ab, wie und wann Sie festlegenPrice
. Wenn Sie zum ersten Mal festlegenPrice
, wird der Wert anDayOpen
weitergegeben. Danach berechnet die App beim FestlegenPrice
dieChange
Eigenschaftenwerte undPercentChange
basierend auf der Differenz zwischenPrice
undDayOpen
.
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
.
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
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieNeues Elementhinzufügen> aus.
Wählen Sie unter Neues Element hinzufügen – SignalR.StockTickerdie Option Installiertes>Visual C#>Web>SignalR und dann SignalR Hub-Klasse (v2) aus.
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.
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(); } } }
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
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieKlassehinzufügen> aus.
Nennen Sie die Klasse StockTicker , und fügen Sie sie dem Projekt hinzu.
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.GetAllStocks
bei 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 updateStockPrice
verfü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:
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieNeues Elementhinzufügen> aus.
Wählen Sie unter Neues Element hinzufügen – SignalR.StockTicker die Option VisualC#>Webinstalliert> aus, und wählen Sie dann OWIN-Startklasse aus.
Nennen Sie die Klasse Startup, und wählen Sie OK aus.
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.
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieHTML-Seitehinzufügen> aus.
Nennen Sie die Datei StockTicker , und wählen Sie OK aus.
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, dieMapHubs
dynamische Dateierstellung im Methodenaufruf zu deaktivieren.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.
Aktualisieren Sie die Skriptverweise im Codeblock so, dass sie den Versionen der Skriptdateien im Projekt entsprechen.
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.
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen SieJavaScript-Dateihinzufügen> aus.
Nennen Sie die Datei StockTicker , und wählen Sie OK aus.
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 start
von 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.
Aktivieren Sie auf der Symbolleiste Skriptdebuggen , und wählen Sie dann die Wiedergabeschaltfläche aus, um die App im Debugmodus auszuführen.
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.
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.
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.
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:
WebSockets, unterstützt von IIS 8 und aktuellen Browsern.
Vom Server gesendete Ereignisse, die von anderen Browsern als internetbasierten Explorer unterstützt werden.
Forever-Frame, unterstützt von Internet Explorer.
Ajax long polling, unterstützt von allen Browsern.
Für jede bestimmte Verbindung wählt SignalR die beste Transportmethode aus, die sowohl vom Server als auch vom Client unterstützt wird.
Öffnen Sie StockTicker.js.
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);
Drücken Sie F5, um das Projekt auszuführen.
Ö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
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie NuGet-Pakete verwalten aus.
Wählen Sie im NuGet-Paket-Manager SignalR.StockTickerdie Option Durchsuchen aus.
Wählen Sie unter Paketquelledie Option nuget.org aus.
Geben Sie SignalR.Sample in das Suchfeld ein, und wählen Sie Microsoft.AspNet.SignalR.Sample>Install aus.
Erweitern Sie in Projektmappen-Explorer den Ordner SignalR.Sample.
Beim Installieren des Pakets SignalR.Sample wurden der Ordner und dessen Inhalt erstellt.
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.
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.
Wählen Sie Open Market (Markt öffnen) aus.
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.
Wählen Sie Markt schließen aus.
Die Tabellenupdates werden beendet.
Der Ticker beendet den Bildlauf.
Klicken Sie auf Zurücksetzen.
Alle Bestandsdaten werden zurückgesetzt.
Die App stellt den Anfangszustand wieder her, bevor die Preisänderungen gestartet werden.
Kopieren Sie die URL aus dem Browser, öffnen Sie zwei andere Browser, und fügen Sie die URLs in die Adressleisten ein.
In jedem Browser werden die gleichen Daten dynamisch gleichzeitig aktualisiert.
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
, CloseMarket
und 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.client
werden, 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 odermarketClosed
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.