Esercitazione: Trasmissione server con SignalR 2
Avviso
Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.
Questa esercitazione illustra come creare un'applicazione Web che usa ASP.NET SignalR 2 per fornire funzionalità di trasmissione server. Trasmissione server significa che il server avvia le comunicazioni inviate ai client.
L'applicazione che verrà creata in questa esercitazione simula un ticker azionario, uno scenario tipico per la funzionalità di trasmissione server. Periodicamente, il server aggiorna in modo casuale i prezzi delle azioni e trasmette gli aggiornamenti a tutti i client connessi. Nel browser i numeri e i simboli nelle colonne Cambia e % cambiano dinamicamente in risposta alle notifiche dal server. Se si aprono altri browser allo stesso URL, vengono visualizzati tutti gli stessi dati e le stesse modifiche ai dati contemporaneamente.
In questa esercitazione:
- Creare il progetto
- Configurare il codice del server
- Esaminare il codice del server
- Configurare il codice client
- Esaminare il codice client
- Test dell'applicazione
- Abilitazione della registrazione
Importante
Se non si vuole eseguire i passaggi della compilazione dell'applicazione, è possibile installare il pacchetto SignalR.Sample in un nuovo progetto Vuoto ASP.NET Applicazione Web. Se si installa il pacchetto NuGet senza eseguire la procedura descritta in questa esercitazione, è necessario seguire le istruzioni nel file readme.txt . Per eseguire il pacchetto è necessario aggiungere una classe di avvio OWIN che chiama il ConfigureSignalR
metodo nel pacchetto installato. Se non si aggiunge la classe di avvio OWIN, verrà visualizzato un errore. Vedere la sezione Installare l'esempio StockTicker di questo articolo.
Prerequisiti
- Visual Studio 2017 con il carico di lavoro Sviluppo ASP.NET e Web.
Creare il progetto
Questa sezione illustra come usare Visual Studio 2017 per creare un'applicazione Web ASP.NET vuota.
In Visual Studio creare un'applicazione Web ASP.NET.
Nella finestra New ASP.NET Web Application - SignalR.StockTicker (Nuova applicazione Web ASP.NET - SignalR.StockTicker ) lasciare selezionata l'opzione Vuoto e selezionare OK.
Configurare il codice del server
In questa sezione viene configurato il codice eseguito nel server.
Creare la classe Stock
Per iniziare, creare la classe modello Stock che verrà usata per archiviare e trasmettere informazioni su un titolo.
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>classe.
Assegnare alla classe il nome Stock e aggiungerlo al progetto.
Sostituire il codice nel file Stock.cs con questo codice:
using System; namespace SignalR.StockTicker { public class Stock { private decimal _price; public string Symbol { get; set; } public decimal Price { get { return _price; } set { if (_price == value) { return; } _price = value; if (DayOpen == 0) { DayOpen = _price; } } } public decimal DayOpen { get; private set; } public decimal Change { get { return Price - DayOpen; } } public double PercentChange { get { return (double)Math.Round(Change / Price, 4); } } } }
Le due proprietà che verranno impostate quando si creano azioni sono
Symbol
(ad esempio, MSFT per Microsoft) ePrice
. Le altre proprietà dipendono da come e quando si impostaPrice
. La prima volta che si impostaPrice
, il valore viene propagato aDayOpen
. Successivamente, quando si impostaPrice
, l'app calcola i valori delleChange
proprietà ePercentChange
in base alla differenza traPrice
eDayOpen
.
Creare le classi StockTickerHub e StockTicker
Si userà l'API dell'hub SignalR per gestire l'interazione da server a client. Una StockTickerHub
classe che deriva dalla classe SignalR Hub
gestirà la ricezione di connessioni e chiamate di metodo dai client. È anche necessario mantenere i dati azionari ed eseguire un Timer
oggetto . L'oggetto Timer
attiverà periodicamente gli aggiornamenti dei prezzi indipendentemente dalle connessioni client. Non è possibile inserire queste funzioni in una Hub
classe, perché Hub sono temporanei. L'app crea un'istanza Hub
di classe per ogni attività nell'hub, ad esempio connessioni e chiamate dal client al server. Quindi il meccanismo che mantiene i dati azionari, aggiorna i prezzi e trasmette gli aggiornamenti dei prezzi deve essere eseguito in una classe separata. Si chiamerà la classe StockTicker
.
Si vuole eseguire una sola istanza della StockTicker
classe nel server, quindi è necessario configurare un riferimento da ogni StockTickerHub
istanza all'istanza singleton StockTicker
. La StockTicker
classe deve trasmettere ai client perché contiene i dati azionari e attiva gli aggiornamenti, ma StockTicker
non è una Hub
classe. La StockTicker
classe deve ottenere un riferimento all'oggetto contesto di connessione dell'hub SignalR. Può quindi usare l'oggetto contesto di connessione SignalR per trasmettere ai client.
Creare StockTickerHub.cs
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo elemento.
In Aggiungi nuovo elemento - SignalR.StockTicker selezionare VisualC#>Web>SignalRinstallato> e quindi selezionare Classe hub SignalR (v2).
Assegnare alla classe il nome StockTickerHub e aggiungerlo al progetto.
Questo passaggio crea il file di classe StockTickerHub.cs . Aggiunge contemporaneamente un set di file di script e riferimenti ad assembly che supporta SignalR al progetto.
Sostituire il codice nel file StockTickerHub.cs con questo codice:
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace SignalR.StockTicker { [HubName("stockTickerMini")] public class StockTickerHub : Hub { private readonly StockTicker _stockTicker; public StockTickerHub() : this(StockTicker.Instance) { } public StockTickerHub(StockTicker stockTicker) { _stockTicker = stockTicker; } public IEnumerable<Stock> GetAllStocks() { return _stockTicker.GetAllStocks(); } } }
Salvare il file.
L'app usa la classe Hub per definire i metodi che i client possono chiamare sul server. Si sta definendo un metodo: GetAllStocks()
. Quando un client si connette inizialmente al server, chiamerà questo metodo per ottenere un elenco di tutti gli stock con i prezzi correnti. Il metodo può essere eseguito in modo sincrono e restituito IEnumerable<Stock>
perché restituisce dati dalla memoria.
Se il metodo deve ottenere i dati eseguendo un'operazione che comporta l'attesa, ad esempio una ricerca di database o una chiamata al servizio Web, è necessario specificare Task<IEnumerable<Stock>>
come valore restituito per abilitare l'elaborazione asincrona. Per altre informazioni, vedere ASP.NET Guida api hub SignalR - Server - Quando eseguire in modo asincrono.
L'attributo HubName
specifica come l'app farà riferimento all'hub nel codice JavaScript nel client. Il nome predefinito nel client, se non si usa questo attributo, è una versione camelCase del nome della classe, che in questo caso sarà stockTickerHub
.
Come si vedrà più avanti quando si crea la StockTicker
classe, l'app crea un'istanza singleton di tale classe nella relativa proprietà statica Instance
. L'istanza singleton di StockTicker
è in memoria indipendentemente dal numero di client che si connettono o si disconnettono. Tale istanza è ciò che il GetAllStocks()
metodo usa per restituire le informazioni sulle scorte correnti.
Creare StockTicker.cs
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>classe.
Assegnare alla classe il nome StockTicker e aggiungerlo al progetto.
Sostituire il codice nel file StockTicker.cs con questo codice:
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace SignalR.StockTicker { public class StockTicker { // Singleton instance private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients)); private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>(); private readonly object _updateStockPricesLock = new object(); //stock can go up or down by a percentage of this factor on each change private readonly double _rangePercent = .002; private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250); private readonly Random _updateOrNotRandom = new Random(); private readonly Timer _timer; private volatile bool _updatingStockPrices = false; private StockTicker(IHubConnectionContext<dynamic> clients) { Clients = clients; _stocks.Clear(); var stocks = new List<Stock> { new Stock { Symbol = "MSFT", Price = 30.31m }, new Stock { Symbol = "APPL", Price = 578.18m }, new Stock { Symbol = "GOOG", Price = 570.30m } }; stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock)); _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval); } public static StockTicker Instance { get { return _instance.Value; } } private IHubConnectionContext<dynamic> Clients { get; set; } public IEnumerable<Stock> GetAllStocks() { return _stocks.Values; } private void UpdateStockPrices(object state) { lock (_updateStockPricesLock) { if (!_updatingStockPrices) { _updatingStockPrices = true; foreach (var stock in _stocks.Values) { if (TryUpdateStockPrice(stock)) { BroadcastStockPrice(stock); } } _updatingStockPrices = false; } } } private bool TryUpdateStockPrice(Stock stock) { // Randomly choose whether to update this stock or not var r = _updateOrNotRandom.NextDouble(); if (r > .1) { return false; } // Update the stock price by a random factor of the range percent var random = new Random((int)Math.Floor(stock.Price)); var percentChange = random.NextDouble() * _rangePercent; var pos = random.NextDouble() > .51; var change = Math.Round(stock.Price * (decimal)percentChange, 2); change = pos ? change : -change; stock.Price += change; return true; } private void BroadcastStockPrice(Stock stock) { Clients.All.updateStockPrice(stock); } } }
Poiché tutti i thread eseguono la stessa istanza del codice StockTicker, la classe StockTicker deve essere thread-safe.
Esaminare il codice del server
Se si esamina il codice del server, sarà utile comprendere il funzionamento dell'app.
Archiviazione dell'istanza singleton in un campo statico
Il codice inizializza il campo statico _instance
che esegue il backup della Instance
proprietà con un'istanza della classe . Poiché il costruttore è privato, è l'unica istanza della classe che l'app può creare. L'app usa l'inizializzazione differita per il _instance
campo. Non è per motivi di prestazioni. È necessario assicurarsi che la creazione dell'istanza sia thread-safe.
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
public static StockTicker Instance
{
get
{
return _instance.Value;
}
}
Ogni volta che un client si connette al server, una nuova istanza della classe StockTickerHub in esecuzione in un thread separato ottiene l'istanza singleton di StockTicker dalla StockTicker.Instance
proprietà statica, come illustrato in precedenza nella StockTickerHub
classe .
Archiviazione dei dati azionari in un oggetto ConcurrentDictionary
Il costruttore inizializza la _stocks
raccolta con alcuni dati azionari di esempio e GetAllStocks
restituisce gli stock. Come illustrato in precedenza, questa raccolta di azioni viene restituita da StockTickerHub.GetAllStocks
, che è un metodo server nella Hub
classe che i client possono chiamare.
private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
private StockTicker(IHubConnectionContext<dynamic> clients)
{
Clients = clients;
_stocks.Clear();
var stocks = new List<Stock>
{
new Stock { Symbol = "MSFT", Price = 30.31m },
new Stock { Symbol = "APPL", Price = 578.18m },
new Stock { Symbol = "GOOG", Price = 570.30m }
};
stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
}
public IEnumerable<Stock> GetAllStocks()
{
return _stocks.Values;
}
La raccolta di titoli è definita come tipo ConcurrentDictionary per thread safety. In alternativa, è possibile usare un oggetto Dictionary e bloccare in modo esplicito il dizionario quando si apportano modifiche.
Per questa applicazione di esempio, è possibile archiviare i dati dell'applicazione in memoria e perdere i dati quando l'app elimina l'istanza StockTicker
. In un'applicazione reale si usa un archivio dati back-end come un database.
Aggiornamento periodico dei prezzi azionari
Il costruttore avvia un Timer
oggetto che chiama periodicamente metodi che aggiornano i prezzi delle azioni in modo casuale.
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
private void UpdateStockPrices(object state)
{
lock (_updateStockPricesLock)
{
if (!_updatingStockPrices)
{
_updatingStockPrices = true;
foreach (var stock in _stocks.Values)
{
if (TryUpdateStockPrice(stock))
{
BroadcastStockPrice(stock);
}
}
_updatingStockPrices = false;
}
}
}
private bool TryUpdateStockPrice(Stock stock)
{
// Randomly choose whether to update this stock or not
var r = _updateOrNotRandom.NextDouble();
if (r > .1)
{
return false;
}
// Update the stock price by a random factor of the range percent
var random = new Random((int)Math.Floor(stock.Price));
var percentChange = random.NextDouble() * _rangePercent;
var pos = random.NextDouble() > .51;
var change = Math.Round(stock.Price * (decimal)percentChange, 2);
change = pos ? change : -change;
stock.Price += change;
return true;
}
Timer
chiama UpdateStockPrices
, che passa null nel parametro di stato. Prima di aggiornare i prezzi, l'app assume un blocco sull'oggetto _updateStockPricesLock
. Il codice controlla se un altro thread sta già aggiornando i prezzi e quindi chiama TryUpdateStockPrice
su ogni stock nell'elenco. Il TryUpdateStockPrice
metodo decide se modificare il prezzo azionario e quanto modificarlo. Se il prezzo azionario cambia, l'app chiama BroadcastStockPrice
per trasmettere la modifica del prezzo azionario a tutti i clienti connessi.
Flag _updatingStockPrices
designato volatile per assicurarsi che sia thread-safe.
private volatile bool _updatingStockPrices = false;
In un'applicazione reale, il TryUpdateStockPrice
metodo chiamerebbe un servizio Web per cercare il prezzo. In questo codice, l'app usa un generatore di numeri casuali per apportare modifiche in modo casuale.
Recupero del contesto SignalR in modo che la classe StockTicker possa trasmettere ai client
Poiché le modifiche al prezzo hanno origine qui nell'oggetto StockTicker
, è l'oggetto che deve chiamare un updateStockPrice
metodo su tutti i client connessi. In una Hub
classe è disponibile un'API per chiamare i metodi client, ma StockTicker
non deriva dalla Hub
classe e non ha un riferimento ad alcun Hub
oggetto. Per trasmettere ai client connessi, la StockTicker
classe deve ottenere l'istanza del contesto SignalR per la StockTickerHub
classe e usarla per chiamare i metodi nei client.
Il codice ottiene un riferimento al contesto SignalR quando crea l'istanza della classe singleton, passa tale riferimento al costruttore e il costruttore lo inserisce nella Clients
proprietà .
Ci sono due motivi per cui vuoi ottenere il contesto una sola volta: ottenere il contesto è un'attività costosa e ottenere una volta che l'app mantiene l'ordine previsto dei messaggi inviati ai client.
private readonly static Lazy<StockTicker> _instance =
new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
private StockTicker(IHubConnectionContext<dynamic> clients)
{
Clients = clients;
// Remainder of constructor ...
}
private IHubConnectionContext<dynamic> Clients
{
get;
set;
}
private void BroadcastStockPrice(Stock stock)
{
Clients.All.updateStockPrice(stock);
}
Ottenere la Clients
proprietà del contesto e inserirla nella StockTickerClient
proprietà consente di scrivere codice per chiamare i metodi client che hanno lo stesso aspetto di una Hub
classe. Ad esempio, per trasmettere a tutti i client è possibile scrivere Clients.All.updateStockPrice(stock)
.
Il updateStockPrice
metodo che si sta chiamando in BroadcastStockPrice
non esiste ancora. Verrà aggiunto in un secondo momento quando si scrive codice che viene eseguito nel client. È possibile fare riferimento qui updateStockPrice
perché Clients.All
è dinamico, il che significa che l'app valuterà l'espressione in fase di esecuzione. Quando questa chiamata al metodo viene eseguita, SignalR invierà il nome del metodo e il valore del parametro al client e, se il client ha un metodo denominato updateStockPrice
, l'app chiamerà tale metodo e passerà il valore del parametro.
Clients.All
significa inviare a tutti i client. SignalR offre altre opzioni per specificare a quali client o gruppi di client inviare. Per altre informazioni, vedere HubConnectionContext.
Registrare la route SignalR
Il server deve sapere quale URL intercettare e indirizzare a SignalR. A tale scopo, aggiungere una classe di avvio OWIN:
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo elemento.
In Aggiungi nuovo elemento - SignalR.StockTicker selezionareVisual C#>Webinstallato> e quindi selezionare Classe di avvio OWIN.
Assegnare alla classe il nome Startup e selezionare OK.
Sostituire il codice predefinito nel file Startup.cs con questo codice:
using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(SignalR.StockTicker.Startup))] namespace SignalR.StockTicker { public class Startup { public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here app.MapSignalR(); } } }
La configurazione del codice del server è stata completata. Nella sezione successiva si configurerà il client.
Configurare il codice client
In questa sezione viene configurato il codice eseguito nel client.
Creare la pagina HTML e il file JavaScript
La pagina HTML visualizzerà i dati e il file JavaScript organizzerà i dati.
Creare StockTicker.html
Prima di tutto, si aggiungerà il client HTML.
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>pagina HTML.
Assegnare al file il nome StockTicker e selezionare OK.
Sostituire il codice predefinito nel file StockTicker.html con questo codice:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>ASP.NET SignalR Stock Ticker</title> <style> body { font-family: 'Segoe UI', Arial, Helvetica, sans-serif; font-size: 16px; } #stockTable table { border-collapse: collapse; } #stockTable table th, #stockTable table td { padding: 2px 6px; } #stockTable table td { text-align: right; } #stockTable .loading td { text-align: left; } </style> </head> <body> <h1>ASP.NET SignalR Stock Ticker Sample</h1> <h2>Live Stock Table</h2> <div id="stockTable"> <table border="1"> <thead> <tr><th>Symbol</th><th>Price</th><th>Open</th><th>Change</th><th>%</th></tr> </thead> <tbody> <tr class="loading"><td colspan="5">loading...</td></tr> </tbody> </table> </div> <!--Script references. --> <!--Reference the jQuery library. --> <script src="/Scripts/jquery-1.10.2.min.js" ></script> <!--Reference the SignalR library. --> <script src="/Scripts/jquery.signalR-2.1.0.js"></script> <!--Reference the autogenerated SignalR hub script. --> <script src="/signalr/hubs"></script> <!--Reference the StockTicker script. --> <script src="StockTicker.js"></script> </body> </html>
Il codice HTML crea una tabella con cinque colonne, una riga di intestazione e una riga di dati con una singola cella che si estende su tutte e cinque le colonne. La riga di dati mostra "caricamento..." momentaneamente all'avvio dell'app. Il codice JavaScript rimuoverà tale riga e aggiungerà al suo posto le righe con i dati di magazzino recuperati dal server.
I tag di script specificano:
File di script jQuery.
File di script core signalR.
File di script proxy SignalR.
Un file di script StockTicker che verrà creato in un secondo momento.
L'app genera dinamicamente il file di script proxy SignalR. Specifica l'URL "/signalr/hubs" e definisce i metodi proxy per i metodi nella classe Hub, in questo caso, per
StockTickerHub.GetAllStocks
. Se si preferisce, è possibile generare questo file JavaScript manualmente usando Le utilità SignalR. Non dimenticare di disabilitare la creazione dinamica dei file nella chiamata alMapHubs
metodo.In Esplora soluzioni espandere Script.
Le librerie di script per jQuery e SignalR sono visibili nel progetto.
Importante
Gestione pacchetti installerà una versione successiva degli script SignalR.
Aggiornare i riferimenti allo script nel blocco di codice in modo che corrispondano alle versioni dei file di script nel progetto.
In Esplora soluzioni fare clic con il pulsante destro del mouse suStockTicker.htmle quindi scegliere Imposta come pagina iniziale.
Creare StockTicker.js
Creare ora il file JavaScript.
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>file JavaScript.
Assegnare al file il nome StockTicker e selezionare OK.
Aggiungere questo codice al file StockTicker.js :
// A simple templating method for replacing placeholders enclosed in curly braces. if (!String.prototype.supplant) { String.prototype.supplant = function (o) { return this.replace(/{([^{}]*)}/g, function (a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r : a; } ); }; } $(function () { var ticker = $.connection.stockTickerMini, // the generated client-side hub proxy up = '▲', down = '▼', $stockTable = $('#stockTable'), $stockTableBody = $stockTable.find('tbody'), rowTemplate = '<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{DayOpen}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>'; function formatStock(stock) { return $.extend(stock, { Price: stock.Price.toFixed(2), PercentChange: (stock.PercentChange * 100).toFixed(2) + '%', Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down }); } function init() { ticker.server.getAllStocks().done(function (stocks) { $stockTableBody.empty(); $.each(stocks, function () { var stock = formatStock(this); $stockTableBody.append(rowTemplate.supplant(stock)); }); }); } // Add a client-side hub method that the server will call ticker.client.updateStockPrice = function (stock) { var displayStock = formatStock(stock), $row = $(rowTemplate.supplant(displayStock)); $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']') .replaceWith($row); } // Start the connection $.connection.hub.start().done(init); });
Esaminare il codice client
Se si esamina il codice client, si apprenderà come il codice client interagisce con il codice del server per rendere l'app funzionante.
Avvio della connessione
$.connection
fa riferimento ai proxy SignalR. Il codice ottiene un riferimento al proxy per la StockTickerHub
classe e lo inserisce nella ticker
variabile . Il nome del proxy è il nome impostato dall'attributo HubName
:
var ticker = $.connection.stockTickerMini
[HubName("stockTickerMini")]
public class StockTickerHub : Hub
Dopo aver definito tutte le variabili e le funzioni, l'ultima riga di codice nel file inizializza la connessione SignalR chiamando la funzione SignalR start
. La start
funzione viene eseguita in modo asincrono e restituisce un oggetto jQuery Deferred. È possibile chiamare la funzione done per specificare la funzione da chiamare quando l'app termina l'azione asincrona.
$.connection.hub.start().done(init);
Ottenere tutte le scorte
La init
funzione chiama la getAllStocks
funzione nel server e usa le informazioni restituite dal server per aggiornare la tabella di magazzino. Si noti che, per impostazione predefinita, è necessario usare camelCasing nel client anche se il nome del metodo è con maiuscole e minuscole pascal nel server. La regola camelCasing si applica solo ai metodi, non agli oggetti. Ad esempio, si fa riferimento a stock.Symbol
e stock.Price
, non stock.symbol
o stock.price
.
function init() {
ticker.server.getAllStocks().done(function (stocks) {
$stockTableBody.empty();
$.each(stocks, function () {
var stock = formatStock(this);
$stockTableBody.append(rowTemplate.supplant(stock));
});
});
}
public IEnumerable<Stock> GetAllStocks()
{
return _stockTicker.GetAllStocks();
}
init
Nel metodo l'app crea codice HTML per una riga di tabella per ogni oggetto stock ricevuto dal server chiamando formatStock
per formattare le proprietà dell'oggetto e quindi chiamando supplant
per sostituire i segnaposto nella rowTemplate
variabile con i valori della proprietà dell'oggetto stock
stock
. Il codice HTML risultante viene quindi aggiunto alla tabella stock.
Nota
Viene chiamato init
passandolo come callback
funzione che viene eseguita al termine della funzione asincrona start
. Se è stata chiamata come istruzione JavaScript separata dopo aver chiamato init
start
, la funzione ha esito negativo perché viene eseguita immediatamente senza attendere che la funzione di avvio finisca di stabilire la connessione. In tal caso, la init
funzione tenterebbe di chiamare la getAllStocks
funzione prima che l'app stabilisca una connessione server.
Ottenere i prezzi aggiornati delle azioni
Quando il server modifica il prezzo di un titolo, chiama su updateStockPrice
client connessi. L'app aggiunge la funzione alla proprietà client del stockTicker
proxy per renderla disponibile per le chiamate dal server.
ticker.client.updateStockPrice = function (stock) {
var displayStock = formatStock(stock),
$row = $(rowTemplate.supplant(displayStock));
$stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
.replaceWith($row);
}
La updateStockPrice
funzione formatta un oggetto stock ricevuto dal server in una riga di tabella esattamente come nella init
funzione. Anziché accodare la riga alla tabella, trova la riga corrente del titolo nella tabella e sostituisce tale riga con quella nuova.
Test dell'applicazione
È possibile testare l'app per assicurarsi che funzioni. Verranno visualizzate tutte le finestre del browser che visualizzano la tabella delle azioni live con prezzi azionari fluttuanti.
Sulla barra degli strumenti attivare Debug script e quindi selezionare il pulsante riproduci per eseguire l'app in modalità debug.
Verrà aperta una finestra del browser in cui è visualizzata la tabella delle scorte aperte. La tabella delle scorte mostra inizialmente il "caricamento..." riga, quindi, dopo un breve periodo di tempo, l'app mostra i dati iniziali delle azioni e quindi i prezzi azionari iniziano a cambiare.
Copiare l'URL dal browser, aprire altri due browser e incollare gli URL nelle barre degli indirizzi.
La visualizzazione iniziale delle scorte è la stessa del primo browser e le modifiche vengono eseguite contemporaneamente.
Chiudere tutti i browser, aprire un nuovo browser e passare allo stesso URL.
L'oggetto singleton StockTicker ha continuato a essere eseguito nel server. La tabella dei titoli live mostra che le azioni hanno continuato a cambiare. La tabella iniziale non viene visualizzata con cifre di modifica zero.
Chiudere il browser.
Abilitazione della registrazione
SignalR dispone di una funzione di registrazione predefinita che è possibile abilitare nel client per facilitare la risoluzione dei problemi. In questa sezione viene abilitata la registrazione e vengono visualizzati esempi che illustrano in che modo i log indicano quali dei metodi di trasporto seguenti usano SignalR:
WebSocket, supportato da IIS 8 e dai browser correnti.
Eventi inviati dal server, supportati dai browser diversi da Internet Explorer.
Frame forever, supportato da Internet Explorer.
Polling lungo Ajax, supportato da tutti i browser.
Per qualsiasi connessione specificata, SignalR sceglie il metodo di trasporto migliore che sia il server che il client supportano.
Aprire StockTicker.js.
Aggiungere questa riga di codice evidenziata per abilitare la registrazione immediatamente prima del codice che inizializza la connessione alla fine del file:
// Start the connection $.connection.hub.logging = true; $.connection.hub.start().done(init);
Premere F5 per eseguire il progetto.
Aprire la finestra degli strumenti di sviluppo del browser e selezionare la console per visualizzare i log. Potrebbe essere necessario aggiornare la pagina per visualizzare i log di SignalR che negoziano il metodo di trasporto per una nuova connessione.
Se si esegue Internet Explorer 10 in Windows 8 (IIS 8), il metodo di trasporto è WebSocket.
Se si esegue Internet Explorer 10 in Windows 7 (IIS 7.5), il metodo di trasporto è iframe.
Se si esegue Firefox 19 in Windows 8 (IIS 8), il metodo di trasporto è WebSockets.
Suggerimento
In Firefox installare il componente aggiuntivo Firebug per ottenere una finestra della console.
Se si esegue Firefox 19 in Windows 7 (IIS 7.5), il metodo di trasporto è eventi inviati dal server .
Installare l'esempio StockTicker
Microsoft.AspNet.SignalR.Sample installa l'applicazione StockTicker. Il pacchetto NuGet include più funzionalità rispetto alla versione semplificata creata da zero. In questa sezione dell'esercitazione viene installato il pacchetto NuGet e vengono esaminate le nuove funzionalità e il codice che li implementa.
Importante
Se si installa il pacchetto senza eseguire i passaggi precedenti di questa esercitazione, è necessario aggiungere una classe di avvio OWIN al progetto. Questo readme.txt file per il pacchetto NuGet illustra questo passaggio.
Installare il pacchetto NuGet SignalR.Sample
In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Gestisci pacchetti NuGet.
In Gestione pacchetti NuGet: SignalR.StockTicker selezionare Sfoglia.
In Origine pacchetto selezionare nuget.org.
Immettere SignalR.Sample nella casella di ricerca e selezionare Installazione microsoft.AspNet.SignalR.Sample>.
In Esplora soluzioni espandere la cartella SignalR.Sample.
L'installazione del pacchetto SignalR.Sample ha creato la cartella e il relativo contenuto.
Nella cartella SignalR.Sample fare clic con il pulsante destro del mouse suStockTicker.htmle quindi scegliere Imposta come pagina iniziale.
Nota
L'installazione del pacchetto NuGet SignalR.Sample potrebbe modificare la versione di jQuery presente nella cartella Scripts . Il nuovo file StockTicker.html installato dal pacchetto nella cartella SignalR.Sample sarà sincronizzato con la versione jQuery installata dal pacchetto, ma se si vuole eseguire nuovamente il file diStockTicker.html originale, potrebbe essere necessario aggiornare prima il riferimento jQuery nel tag script.
Eseguire l'applicazione
La tabella visualizzata nella prima app include funzionalità utili. L'applicazione ticker completa mostra nuove funzionalità: una finestra di scorrimento orizzontale che mostra i dati azionari e le azioni che cambiano colore man mano che aumentano e diminuiscono.
Premere F5 per eseguire l'app.
Quando si esegue l'app per la prima volta, il "mercato" è "chiuso" e viene visualizzata una tabella statica e una finestra ticker che non scorre.
Selezionare Apri mercato.
La casella Live Stock Ticker inizia a scorrere orizzontalmente e il server inizia a trasmettere periodicamente le variazioni dei prezzi delle azioni su base casuale.
Ogni volta che un prezzo azionario cambia, l'app aggiorna sia la tabella azionaria live che il Live Stock Ticker.
Quando la variazione del prezzo di un titolo è positiva, l'app mostra il titolo con uno sfondo verde.
Quando la modifica è negativa, l'app mostra il titolo con uno sfondo rosso.
Selezionare Chiudi mercato.
La tabella viene aggiornata.
Il ticker smette di scorrere.
Selezionare Reimposta.
Tutti i dati azionari vengono reimpostati.
L'app ripristina lo stato iniziale prima dell'avvio delle modifiche al prezzo.
Copiare l'URL dal browser, aprire altri due browser e incollare gli URL nelle barre degli indirizzi.
In ogni browser vengono visualizzati gli stessi dati aggiornati dinamicamente.
Quando si seleziona uno dei controlli, tutti i browser rispondono allo stesso modo contemporaneamente.
Visualizzazione di Live Stock Ticker
La visualizzazione Live Stock Ticker è un elenco non ordinato in un elemento formattato in una <div>
singola riga in base agli stili CSS. L'app inizializza e aggiorna il ticker allo stesso modo della tabella: sostituendo i segnaposto in una <li>
stringa di modello e aggiungendo dinamicamente gli <li>
elementi all'elemento <ul>
. L'app include lo scorrimento usando la funzione jQuery animate
per variare il margine sinistro dell'elenco non ordinato all'interno di <div>
.
StockTicker.html SignalR.Sample
Il codice HTML ticker stock:
<h2>Live Stock Ticker</h2>
<div id="stockTicker">
<div class="inner">
<ul>
<li class="loading">loading...</li>
</ul>
</div>
</div>
SignalR.Sample StockTicker.css
Codice CSS di ticker azionario:
#stockTicker {
overflow: hidden;
width: 450px;
height: 24px;
border: 1px solid #999;
}
#stockTicker .inner {
width: 9999px;
}
#stockTicker ul {
display: inline-block;
list-style-type: none;
margin: 0;
padding: 0;
}
#stockTicker li {
display: inline-block;
margin-right: 8px;
}
/*<li data-symbol="{Symbol}"><span class="symbol">{Symbol}</span><span class="price">{Price}</span><span class="change">{PercentChange}</span></li>*/
#stockTicker .symbol {
font-weight: bold;
}
#stockTicker .change {
font-style: italic;
}
SignalR.StockTicker.js SignalR.Sample
Il codice jQuery che lo rende scorrevole:
function scrollTicker() {
var w = $stockTickerUl.width();
$stockTickerUl.css({ marginLeft: w });
$stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
}
Metodi aggiuntivi nel server che il client può chiamare
Per aggiungere flessibilità all'app, sono disponibili metodi aggiuntivi che l'app può chiamare.
SignalR.Sample StockTickerHub.cs
La StockTickerHub
classe definisce quattro metodi aggiuntivi che il client può chiamare:
public string GetMarketState()
{
return _stockTicker.MarketState.ToString();
}
public void OpenMarket()
{
_stockTicker.OpenMarket();
}
public void CloseMarket()
{
_stockTicker.CloseMarket();
}
public void Reset()
{
_stockTicker.Reset();
}
L'app chiama OpenMarket
, CloseMarket
e Reset
in risposta ai pulsanti nella parte superiore della pagina. Illustrano il modello di un client che attiva una modifica dello stato immediatamente propagato a tutti i client. Ognuno di questi metodi chiama un metodo nella StockTicker
classe che causa la modifica dello stato del mercato e quindi trasmette il nuovo stato.
SignalR.Sample StockTicker.cs
StockTicker
Nella classe l'app mantiene lo stato del mercato con una MarketState
proprietà che restituisce un MarketState
valore enumerazione:
public MarketState MarketState
{
get { return _marketState; }
private set { _marketState = value; }
}
public enum MarketState
{
Closed,
Open
}
Ognuno dei metodi che modificano lo stato del mercato lo fa all'interno di un blocco di blocco perché la StockTicker
classe deve essere thread-safe:
public void OpenMarket()
{
lock (_marketStateLock)
{
if (MarketState != MarketState.Open)
{
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
MarketState = MarketState.Open;
BroadcastMarketStateChange(MarketState.Open);
}
}
}
public void CloseMarket()
{
lock (_marketStateLock)
{
if (MarketState == MarketState.Open)
{
if (_timer != null)
{
_timer.Dispose();
}
MarketState = MarketState.Closed;
BroadcastMarketStateChange(MarketState.Closed);
}
}
}
public void Reset()
{
lock (_marketStateLock)
{
if (MarketState != MarketState.Closed)
{
throw new InvalidOperationException("Market must be closed before it can be reset.");
}
LoadDefaultStocks();
BroadcastMarketReset();
}
}
Per assicurarsi che questo codice sia thread-safe, il campo che esegue il _marketState
backup della MarketState
proprietà designata volatile
:
private volatile MarketState _marketState;
I BroadcastMarketStateChange
metodi e BroadcastMarketReset
sono simili al metodo BroadcastStockPrice già visto, ad eccezione del fatto che chiamano metodi diversi definiti nel client:
private void BroadcastMarketStateChange(MarketState marketState)
{
switch (marketState)
{
case MarketState.Open:
Clients.All.marketOpened();
break;
case MarketState.Closed:
Clients.All.marketClosed();
break;
default:
break;
}
}
private void BroadcastMarketReset()
{
Clients.All.marketReset();
}
Funzioni aggiuntive nel client che il server può chiamare
La updateStockPrice
funzione ora gestisce sia la tabella che la visualizzazione ticker e usa jQuery.Color
per lampeggiare i colori rosso e verde.
Nuove funzioni in SignalR.StockTicker.js abilitare e disabilitare i pulsanti in base allo stato del mercato. Arrestano o avviano lo scorrimento orizzontale live stock Ticker . Poiché molte funzioni vengono aggiunte a ticker.client
, l'app usa la funzione extend jQuery per aggiungerle.
$.extend(ticker.client, {
updateStockPrice: function (stock) {
var displayStock = formatStock(stock),
$row = $(rowTemplate.supplant(displayStock)),
$li = $(liTemplate.supplant(displayStock)),
bg = stock.LastChange === 0
? '255,216,0' // yellow
: stock.LastChange > 0
? '154,240,117' // green
: '255,148,148'; // red
$stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
.replaceWith($row);
$stockTickerUl.find('li[data-symbol=' + stock.Symbol + ']')
.replaceWith($li);
$row.flash(bg, 1000);
$li.flash(bg, 1000);
},
marketOpened: function () {
$("#open").prop("disabled", true);
$("#close").prop("disabled", false);
$("#reset").prop("disabled", true);
scrollTicker();
},
marketClosed: function () {
$("#open").prop("disabled", false);
$("#close").prop("disabled", true);
$("#reset").prop("disabled", false);
stopTicker();
},
marketReset: function () {
return init();
}
});
Configurazione client aggiuntiva dopo aver stabilito la connessione
Dopo aver stabilito la connessione, il client ha alcune operazioni aggiuntive da eseguire:
Scopri se il mercato è aperto o chiuso per chiamare la funzione o
marketClosed
appropriatamarketOpened
.Collegare le chiamate al metodo server ai pulsanti.
$.connection.hub.start()
.pipe(init)
.pipe(function () {
return ticker.server.getMarketState();
})
.done(function (state) {
if (state === 'Open') {
ticker.client.marketOpened();
} else {
ticker.client.marketClosed();
}
// Wire up the buttons
$("#open").click(function () {
ticker.server.openMarket();
});
$("#close").click(function () {
ticker.server.closeMarket();
});
$("#reset").click(function () {
ticker.server.reset();
});
});
I metodi del server non vengono collegati ai pulsanti fino a quando l'app non stabilisce la connessione. È quindi possibile che il codice non possa chiamare i metodi del server prima che siano disponibili.
Risorse aggiuntive
In questa esercitazione si è appreso come programmare un'applicazione SignalR che trasmette i messaggi dal server a tutti i client connessi. Ora è possibile trasmettere i messaggi su base periodica e in risposta alle notifiche da qualsiasi client. È possibile usare il concetto di istanza singleton multithread per mantenere lo stato del server in scenari di gioco online multi-giocatore. Per un esempio, vedi il gioco ShootR basato su SignalR.
Per esercitazioni che mostrano scenari di comunicazione peer-to-peer, vedere Introduzione con SignalR e Aggiornamento in tempo reale con SignalR.
Per altre informazioni su SignalR, vedere le risorse seguenti:
Passaggi successivi
In questa esercitazione:
- Creazione del progetto
- Configurare il codice del server
- Esaminare il codice del server
- Configurare il codice client
- Esaminare il codice client
- Testare l'applicazione
- Registrazione abilitata
Passare all'articolo successivo per informazioni su come creare un'applicazione Web in tempo reale che usa ASP.NET SignalR 2.