Öğretici: SignalR 2 ile sunucu yayını
Uyarı
Bu belgeler SignalR'nin en son sürümüne yönelik değildir. SignalR ASP.NET Core göz atın.
Bu öğreticide, sunucu yayını işlevselliği sağlamak için ASP.NET SignalR 2 kullanan bir web uygulamasının nasıl oluşturulacağı gösterilmektedir. Sunucu yayını, sunucunun istemcilere gönderilen iletişimleri başlattığı anlamına gelir.
Bu öğreticide oluşturacağınız uygulama, sunucu yayını işlevselliği için tipik bir senaryo olan hisse senedi değerleyicisinin benzetimini yapar. Sunucu düzenli aralıklarla hisse senedi fiyatlarını rastgele güncelleştirir ve güncelleştirmeleri tüm bağlı istemcilere yayınlar. Tarayıcıda, Değiştir ve sütunlardaki sayılar ve % simgeler sunucudan gelen bildirimlere yanıt olarak dinamik olarak değişir. Aynı URL'ye ek tarayıcılar açarsanız, hepsi aynı verileri ve aynı değişiklikleri aynı anda verilerde gösterir.
Bu öğreticide şunları yaptınız:
- Proje oluşturma
- Sunucu kodunu ayarlama
- Sunucu kodunu inceleme
- İstemci kodunu ayarlama
- İstemci kodunu inceleme
- Uygulamayı test edin
- Günlü kaydını etkinleştir
Önemli
Uygulamayı oluşturma adımlarında çalışmak istemiyorsanız SignalR.Sample paketini yeni bir Boş ASP.NET Web Uygulaması projesine yükleyebilirsiniz. NuGet paketini bu öğreticideki adımları uygulamadan yüklerseniz readme.txt dosyasındaki yönergeleri izlemeniz gerekir. Paketi çalıştırmak için yüklü pakette yöntemini çağıran ConfigureSignalR
bir OWIN başlangıç sınıfı eklemeniz gerekir. OWIN başlangıç sınıfını eklemezseniz bir hata alırsınız. Bu makalenin StockTicker örneğini yükleme bölümüne bakın.
Önkoşullar
- ASP.NET ve web geliştirme iş yüküyle Visual Studio 2017.
Proje oluşturma
Bu bölümde, Boş bir ASP.NET Web Uygulaması oluşturmak için Visual Studio 2017'nin nasıl kullanılacağı gösterilmektedir.
Visual Studio'da bir ASP.NET Web Uygulaması oluşturun.
Yeni ASP.NET Web Uygulaması - SignalR.StockTicker penceresinde Boş'u seçili bırakın ve Tamam'ı seçin.
Sunucu kodunu ayarlama
Bu bölümde, sunucuda çalışan kodu ayarlarsınız.
Stock sınıfını oluşturma
İlk olarak , bir hisse senediyle ilgili bilgileri depolamak ve iletmek için kullanacağınız Hisse Senedi modeli sınıfını oluşturursunuz.
Çözüm Gezgini'da projeye sağ tıklayın veSınıfEkle'yi> seçin.
Sınıfı Stock olarak adlandırın ve projeye ekleyin.
Stock.cs dosyasındaki kodu şu kodla değiştirin:
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); } } } }
Hisse senetlerini oluştururken ayarlayacağınız iki özellik (örneğin, Microsoft için MSFT) ve
Price
şeklindedirSymbol
. Diğer özellikler, öğesini nasıl ve ne zaman ayarladığınızaPrice
bağlıdır. değerini ilk kez ayarladığınızdaPrice
, değer öğesineDayOpen
yayılır. Bundan sonra, ayarladığınızdaPrice
uygulama ile arasındakiPrice
DayOpen
farka göre vePercentChange
özellik değerlerini hesaplarChange
.
StockTickerHub ve StockTicker sınıflarını oluşturma
Sunucudan istemciye etkileşimi işlemek için SignalR Hub API'sini kullanacaksınız. StockTickerHub
SignalR Hub
sınıfından türetilen bir sınıf, istemcilerden gelen alma bağlantılarını ve yöntem çağrılarını işler. Ayrıca hisse senedi verilerini korumanız ve bir Timer
nesne çalıştırmanız gerekir. nesnesi, Timer
istemci bağlantılarından bağımsız olarak fiyat güncelleştirmelerini düzenli aralıklarla tetikler. Hub'lar geçici olduğundan bu işlevleri bir Hub
sınıfa koyamazsınız. Uygulama, merkezdeki her görev için istemciden sunucuya bağlantılar ve çağrılar gibi bir Hub
sınıf örneği oluşturur. Bu nedenle hisse senedi verilerini tutan, fiyatları güncelleştiren ve fiyat güncelleştirmelerini yayınlayan mekanizmanın ayrı bir sınıfta çalıştırılması gerekir. Sınıfı StockTicker
olarak adlandıracaksınız.
Sınıfının yalnızca bir örneğinin StockTicker
sunucuda çalışmasını istiyorsunuz, bu nedenle her StockTickerHub
örnekten tekil StockTicker
örneğe bir başvuru ayarlamanız gerekir. Sınıfın StockTicker
istemcilere yayınlaması gerekir çünkü hisse senedi verilerine sahiptir ve güncelleştirmeleri tetikler, ancak StockTicker
bir Hub
sınıf değildir. sınıfının StockTicker
SignalR Hub bağlantı bağlam nesnesine bir başvuru alması gerekir. Daha sonra istemcilere yayınlamak için SignalR bağlantı bağlamı nesnesini kullanabilir.
StockTickerHub.cs oluşturma
Çözüm Gezgini'da projeye sağ tıklayın veYeni Öğe Ekle'yi> seçin.
Yeni Öğe Ekle - SignalR.StockTicker bölümünde Yüklü>Visual C#>Web>SignalR'yi ve ardından SignalR Hub Sınıfı (v2) öğesini seçin.
Sınıfı StockTickerHub olarak adlandırın ve projeye ekleyin.
Bu adım StockTickerHub.cs sınıf dosyasını oluşturur. Aynı anda, projeye SignalR'yi destekleyen bir dizi betik dosyası ve derleme başvurusu ekler.
StockTickerHub.cs dosyasındaki kodu şu kodla değiştirin:
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(); } } }
Dosyayı kaydedin.
Uygulama, istemcilerin sunucuda çağırabileceği yöntemleri tanımlamak için Hub sınıfını kullanır. Tek bir yöntem tanımlıyorsunuz: GetAllStocks()
. bir istemci başlangıçta sunucuya bağlandığında, geçerli fiyatlarıyla tüm hisse senetlerinin listesini almak için bu yöntemi çağırır. yöntemi zaman uyumlu olarak çalışabilir ve bellekten veri döndüreceği için döndürebilir IEnumerable<Stock>
.
Yöntemin, veritabanı araması veya web hizmeti çağrısı gibi beklemeyi kapsayan bir işlem yaparak verileri alması gerekiyorsa, zaman uyumsuz işlemeyi etkinleştirmek için dönüş değeri olarak belirtebilirsiniz Task<IEnumerable<Stock>>
. Daha fazla bilgi için bkz. ASP.NET SignalR Hubs API Kılavuzu - Sunucu - Zaman uyumsuz olarak yürütülme zamanları.
özniteliği, HubName
uygulamanın istemcideki JavaScript kodunda Hub'a nasıl başvuracağını belirtir. Bu özniteliği kullanmazsanız istemcideki varsayılan ad, sınıf adının camelCase sürümüdür ve bu durumda olacaktır stockTickerHub
.
Daha sonra sınıfı oluşturduğunuzda StockTicker
göreceğiniz gibi uygulama, statik Instance
özelliğinde bu sınıfın tekil bir örneğini oluşturur. Bu tekil örneği StockTicker
, kaç istemcinin bağlanması veya bağlantısının kesilmesi fark etmez bellektedir. Bu örnek, yöntemin GetAllStocks()
geçerli hisse senedi bilgilerini döndürmek için kullandığı örnektir.
StockTicker.cs oluşturma
Çözüm Gezgini'da projeye sağ tıklayın veSınıfEkle'yi> seçin.
Sınıfı StockTicker olarak adlandırın ve projeye ekleyin.
StockTicker.cs dosyasındaki kodu şu kodla değiştirin:
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); } } }
Tüm iş parçacıkları aynı StockTicker kodu örneğini çalıştıracağı için StockTicker sınıfının iş parçacığı güvenli olması gerekir.
Sunucu kodunu inceleme
Sunucu kodunu incelerseniz, uygulamanın nasıl çalıştığını anlamanıza yardımcı olur.
Tekil örneği statik alanda depolama
Kod, özelliğini sınıfının bir örneğiyle destekleyen Instance
statik _instance
alanı başlatır. Oluşturucu özel olduğundan, uygulamanın oluşturabileceği tek sınıf örneğidir. Uygulama, alan için Gecikmeli başlatma kullanır _instance
. Performans nedenleriyle değil. Örnek oluşturma işleminin iş parçacığı açısından güvenli olduğundan emin olmaktır.
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
public static StockTicker Instance
{
get
{
return _instance.Value;
}
}
İstemci sunucuya her bağlandığında, ayrı bir iş parçacığında çalıştırılan stockTickerHub sınıfının yeni bir örneği, daha önce StockTickerHub
sınıfında gördüğünüz gibi statik özelliğinden StockTicker.Instance
StockTicker tekil örneğini alır.
Hisse senedi verilerini ConcurrentDictionary'de depolama
Oluşturucu, koleksiyonu bazı örnek hisse senedi verileriyle başlatır _stocks
ve GetAllStocks
hisse senetlerini döndürür. Daha önce gördüğünüz gibi, bu hisse senetleri koleksiyonu tarafından döndürülür StockTickerHub.GetAllStocks
. Bu, istemcilerinin çağırabileceği sınıftaki Hub
bir sunucu yöntemidir.
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;
}
Hisse senetleri koleksiyonu, iş parçacığı güvenliği için ConcurrentDictionary türü olarak tanımlanır. Alternatif olarak, bir Sözlük nesnesi kullanabilir ve sözlüğe değişiklik yaptığınızda sözlüğü açıkça kilitleyebilirsiniz.
Bu örnek uygulama için, uygulama verilerini bellekte depolamak ve uygulama örneği attığında StockTicker
verileri kaybetmek uygun olur. Gerçek bir uygulamada veritabanı gibi bir arka uç veri deposuyla çalışırsınız.
Hisse senedi fiyatlarını düzenli aralıklarla güncelleştirme
Oluşturucu, hisse senedi fiyatlarını rastgele olarak güncelleştiren yöntemleri düzenli aralıklarla çağıran bir nesne başlatır Timer
.
_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
UpdateStockPrices
çağrıları, durum parametresinde null değerini geçirir. Fiyatları güncelleştirmeden önce uygulama nesneye kilitlenir _updateStockPricesLock
. Kod, başka bir iş parçacığının zaten fiyatları güncelleştirip güncelleştirmediğini denetler ve listedeki her hisse senedini çağırır TryUpdateStockPrice
. TryUpdateStockPrice
yöntemi hisse senedi fiyatının değiştirilip değiştirilmeyeceğini ve ne kadar değiştireceğini karar verir. Hisse senedi fiyatı değişirse uygulama, hisse senedi fiyat değişikliğini tüm bağlı istemcilere yayınlamayı çağırır BroadcastStockPrice
.
bayrağı, _updatingStockPrices
iş parçacığı güvenli olduğundan emin olmak için geçici olarak belirlenmiştir.
private volatile bool _updatingStockPrices = false;
Gerçek bir uygulamada yöntemi, TryUpdateStockPrice
fiyatı aramak için bir web hizmetini çağırır. Bu kodda uygulama rastgele değişiklik yapmak için rastgele bir sayı oluşturucu kullanır.
StockTicker sınıfının istemcilere yayın edebilmesi için SignalR bağlamını alma
Fiyat değişiklikleri burada nesnesinden StockTicker
kaynaklandığı için, tüm bağlı istemcilerde bir updateStockPrice
yöntemi çağırması gereken nesnedir. Bir Hub
sınıfta, istemci yöntemlerini çağırmak için bir API'niz vardır, ancak StockTicker
sınıfından Hub
türetilmez ve herhangi Hub
bir nesneye başvurusu yoktur. Bağlı istemcilere yayınlamak için sınıfın StockTicker
sınıfın SignalR bağlam örneğini alması ve istemcilerdeki yöntemleri çağırmak için StockTickerHub
bunu kullanması gerekir.
Kod, tekli sınıf örneğini oluşturduğunda SignalR bağlamı için bir başvuru alır, bu başvuruyu oluşturucuya geçirir ve oluşturucu bunu özelliğine Clients
yerleştirir.
Bağlamı yalnızca bir kez almak istemenin iki nedeni vardır: bağlamı almak pahalı bir görevdir ve bir kez almak, uygulamanın istemcilere gönderilen iletilerin hedeflenen sırasını koruduğundan emin olmanızı sağlar.
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);
}
Bağlamın Clients
özelliğini alma ve özelliğine StockTickerClient
yerleştirme, bir sınıfta olduğu gibi Hub
görünen istemci yöntemlerini çağırmak için kod yazmanızı sağlar. Örneğin, tüm istemcilere yayınlamak için yazabilirsiniz Clients.All.updateStockPrice(stock)
.
updateStockPrice
Çağırdığınız BroadcastStockPrice
yöntem henüz mevcut değil. Daha sonra istemcide çalışan bir kod yazdığınızda ekleyeceksiniz. Dinamik olduğundan buraya Clients.All
başvurabilirsinizupdateStockPrice
; bu da uygulamanın ifadeyi çalışma zamanında değerlendireceği anlamına gelir. Bu yöntem çağrısı yürütürken SignalR yöntem adını ve parametre değerini istemciye gönderir ve istemcinin adlı updateStockPrice
bir yöntemi varsa, uygulama bu yöntemi çağırır ve parametre değerini ona geçirir.
Clients.All
tüm istemcilere gönderme anlamına gelir. SignalR, hangi istemcilere veya istemci gruplarına gönderileceğini belirtmek için size başka seçenekler sunar. Daha fazla bilgi için bkz . HubConnectionContext.
SignalR yolunu kaydetme
Sunucunun hangi URL'nin kesilip SignalR'ye yönlendirilmesi gerektiğini bilmesi gerekir. Bunu yapmak için bir OWIN başlangıç sınıfı ekleyin:
Çözüm Gezgini'da projeye sağ tıklayın veYeni Öğe Ekle'yi> seçin.
Yeni Öğe Ekle - SignalR.StockTicker'daYüklü>Visual C#>Web'i ve ardından OWIN Başlangıç Sınıfı'ni seçin.
Sınıfı Başlangıç olarak adlandırın ve Tamam'ı seçin.
Startup.cs dosyasındaki varsayılan kodu şu kodla değiştirin:
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(); } } }
Sunucu kodunu ayarlamayı tamamladınız. Sonraki bölümde istemciyi ayarlayacaksınız.
İstemci kodunu ayarlama
Bu bölümde, istemcide çalışan kodu ayarlarsınız.
HTML sayfasını ve JavaScript dosyasını oluşturma
HTML sayfasında veriler görüntülenir ve JavaScript dosyası verileri düzenler.
StockTicker.html oluşturma
İlk olarak HTML istemcisini ekleyeceksiniz.
Çözüm Gezgini'da projeye sağ tıklayın veHTML SayfasıEkle'yi> seçin.
Dosyayı StockTicker olarak adlandırın ve Tamam'ı seçin.
StockTicker.html dosyasındaki varsayılan kodu şu kodla değiştirin:
<!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>
HTML beş sütunlu bir tablo, üst bilgi satırı ve beş sütunun tümüne yayılan tek bir hücre içeren bir veri satırı oluşturur. Veri satırı "yükleniyor..." bir an için uygulama başlatıldığında. JavaScript kodu bu satırı kaldırır ve sunucudan alınan hisse senedi verileriyle yerine satır ekler.
Betik etiketleri şunları belirtir:
jQuery betik dosyası.
SignalR çekirdek betik dosyası.
SignalR proxy'leri betik dosyası.
Daha sonra oluşturacağınız bir StockTicker betik dosyası.
Uygulama SignalR proxy'leri betik dosyasını dinamik olarak oluşturur. "/signalr/hubs" URL'sini belirtir ve Hub sınıfındaki yöntemler için ara sunucu yöntemlerini tanımlar. Bu örnekte için
StockTickerHub.GetAllStocks
. İsterseniz SignalR Yardımcı Programlarını kullanarak bu JavaScript dosyasını el ile oluşturabilirsiniz. Yöntem çağrısındaMapHubs
dinamik dosya oluşturmayı devre dışı bırakmayı unutmayın.Çözüm Gezgini'daBetikler'i genişletin.
jQuery ve SignalR için betik kitaplıkları projede görünür.
Önemli
Paket yöneticisi SignalR betiklerinin sonraki bir sürümünü yükler.
Kod bloğundaki betik başvurularını projedeki betik dosyalarının sürümlerine karşılık gelen şekilde güncelleştirin.
Çözüm Gezgini'daStockTicker.htmlöğesine sağ tıklayın ve Ardından Başlangıç Sayfası Olarak Ayarla'yı seçin.
StockTicker.js oluşturma
Şimdi JavaScript dosyasını oluşturun.
Çözüm Gezgini'da projeye sağ tıklayın veJavaScript DosyasıEkle'yi> seçin.
Dosyayı StockTicker olarak adlandırın ve Tamam'ı seçin.
Bu kodu StockTicker.js dosyasına ekleyin:
// 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); });
İstemci kodunu inceleme
İstemci kodunu incelerseniz, uygulamanın çalışması için istemci kodunun sunucu koduyla nasıl etkileşim kuracağını öğrenmenize yardımcı olur.
Bağlantıyı başlatma
$.connection
SignalR proxy'lerini ifade eder. Kod, sınıfın StockTickerHub
ara sunucusuna bir başvuru alır ve değişkenine ticker
yerleştirir. Proxy adı, özniteliği tarafından HubName
ayarlanan addır:
var ticker = $.connection.stockTickerMini
[HubName("stockTickerMini")]
public class StockTickerHub : Hub
Tüm değişkenleri ve işlevleri tanımladıktan sonra, dosyadaki son kod satırı SignalR işlevini çağırarak SignalR start
bağlantısını başlatır. start
İşlev zaman uyumsuz olarak yürütülür ve jQuery Ertelenmiş nesnesi döndürür. Uygulama zaman uyumsuz eylemi tamamladığında çağrılacak işlevi belirtmek için done işlevini çağırabilirsiniz.
$.connection.hub.start().done(init);
Tüm hisse senetlerini alma
İşlev, init
sunucudaki işlevi çağırır getAllStocks
ve sunucunun hisse senedi tablosunu güncelleştirmek için döndürdüğü bilgileri kullanır. Varsayılan olarak, yöntem adı sunucuda pascal-cased olsa bile istemcide camelCasing kullanmanız gerekir. camelCasing kuralı nesnelere değil yalnızca yöntemlere uygulanır. Örneğin, veya stock.price
yerine stock.symbol
ve stock.Price
öğesine başvurursunuzstock.Symbol
.
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();
}
yönteminde init
uygulama, nesnenin özelliklerini stock
biçimlendirmek için çağrısı formatStock
yaparak sunucudan alınan her hisse senedi nesnesi için bir tablo satırı için HTML oluşturur ve ardından değişkendeki rowTemplate
yer tutucuları nesne özelliği değerleriyle stock
değiştirmek için çağrısı supplant
yapar. Elde edilen HTML daha sonra hisse senedi tablosuna eklenir.
Not
Zaman uyumsuz start
işlev tamamlandıktan sonra yürütülen bir callback
işlev olarak geçirerek çağırıninit
. çağrısından start
sonra ayrı bir JavaScript deyimi olarak çağırdıysanızinit
, start işlevinin bağlantıyı kurmayı bitirmesini beklemeden hemen çalıştırıldığından işlev başarısız olur. Bu durumda işlev, init
uygulama bir sunucu bağlantısı kurmadan önce işlevini çağırmayı getAllStocks
dener.
Güncelleştirilmiş hisse senedi fiyatlarını alma
Sunucu hisse senedinin fiyatını değiştirdiğinde bağlı istemcileri çağırır updateStockPrice
. Uygulama, işlevi sunucudan gelen çağrılar için kullanılabilir hale getirmek için proxy'nin stockTicker
istemci özelliğine ekler.
ticker.client.updateStockPrice = function (stock) {
var displayStock = formatStock(stock),
$row = $(rowTemplate.supplant(displayStock));
$stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
.replaceWith($row);
}
İşlev, updateStockPrice
sunucudan alınan hisse senedi nesnesini işlevdeki gibi bir tablo satırına biçimlendirir init
. Satırı tabloya eklemek yerine, tablonun hisse senedinin geçerli satırını bulur ve bu satırı yenisiyle değiştirir.
Uygulamayı test edin
Çalıştığından emin olmak için uygulamayı test edebilirsiniz. Tüm tarayıcı pencerelerinin hisse senedi fiyatlarının dalgalı olduğu canlı hisse senedi tablosunu görüntülediğini göreceksiniz.
Araç çubuğunda Betik Hata Ayıklama'yı açın ve uygulamayı Hata Ayıklama modunda çalıştırmak için yürüt düğmesini seçin.
Live Stock Table'ın görüntülendiği bir tarayıcı penceresi açılır. Hisse senedi tablosunda başlangıçta "yükleniyor..." satırına gidin ve kısa bir süre sonra uygulama ilk hisse senedi verilerini gösterir ve ardından hisse senedi fiyatları değişmeye başlar.
Tarayıcıdan URL'yi kopyalayın, iki tarayıcı daha açın ve URL'leri adres çubuklarına yapıştırın.
İlk stok ekranı ilk tarayıcıyla aynıdır ve değişiklikler aynı anda gerçekleşir.
Tüm tarayıcıları kapatın, yeni bir tarayıcı açın ve aynı URL'ye gidin.
StockTicker singleton nesnesi sunucuda çalışmaya devam etti. Live Stock Table hisse senetlerinin değişmeye devam ettiğini gösterir. Sıfır değişiklik rakamı içeren ilk tabloyu görmezsiniz.
Tarayıcıyı kapatın.
Günlü kaydını etkinleştir
SignalR, sorun gidermeye yardımcı olması için istemcide etkinleştirebileceğiniz yerleşik bir günlüğe kaydetme işlevine sahiptir. Bu bölümde, günlüğe kaydetmeyi etkinleştirip günlüklerin SignalR'nin aşağıdaki aktarım yöntemlerinden hangisini kullandığını nasıl gösterdiğini gösteren örnekleri görürsünüz:
IIS 8 ve geçerli tarayıcılar tarafından desteklenen WebSockets.
Internet Explorer dışındaki tarayıcılar tarafından desteklenen sunucu tarafından gönderilen olaylar.
Forever frame, Internet Explorer tarafından desteklenir.
Ajax uzun yoklama, tüm tarayıcılar tarafından desteklenir.
Belirli bir bağlantı için SignalR, hem sunucunun hem de istemcinin desteklediği en iyi aktarım yöntemini seçer.
StockTicker.jsaçın.
Dosyanın sonunda bağlantıyı başlatan koddan hemen önce günlüğe kaydetmeyi etkinleştirmek için bu vurgulanmış kod satırını ekleyin:
// Start the connection $.connection.hub.logging = true; $.connection.hub.start().done(init);
Projeyi çalıştırmak için F5 tuşuna basın.
Tarayıcınızın geliştirici araçları penceresini açın ve günlükleri görmek için Konsol'a tıklayın. SignalR günlüklerinin yeni bir bağlantı için aktarım yönteminde anlaşmaya varan günlüklerini görmek için sayfayı yenilemeniz gerekebilir.
Internet Explorer 10'u Windows 8 (IIS 8) üzerinde çalıştırıyorsanız aktarım yöntemi WebSockets'tir.
Windows 7'de (IIS 7.5) Internet Explorer 10 çalıştırıyorsanız, aktarım yöntemi iframe'dir.
firefox 19'u Windows 8 (IIS 8) üzerinde çalıştırıyorsanız aktarım yöntemi WebSockets'tir.
İpucu
Firefox'ta Bir Konsol penceresi almak için Firebug eklentisini yükleyin.
Windows 7'de (IIS 7.5) Firefox 19 çalıştırıyorsanız, aktarım yöntemi sunucu tarafından gönderilen olaylardır.
StockTicker örneğini yükleme
Microsoft.AspNet.SignalR.Sample, StockTicker uygulamasını yükler. NuGet paketi sıfırdan oluşturduğunuz basitleştirilmiş sürümden daha fazla özellik içerir. Öğreticinin bu bölümünde NuGet paketini yükler ve yeni özellikleri ve bunları uygulayan kodu gözden geçirirsiniz.
Önemli
Bu öğreticinin önceki adımlarını gerçekleştirmeden paketi yüklerseniz, projenize bir OWIN başlangıç sınıfı eklemeniz gerekir. NuGet paketi için bu readme.txt dosyası bu adımı açıklar.
SignalR.Sample NuGet paketini yükleme
Çözüm Gezgini'da projeye sağ tıklayın ve NuGet Paketlerini Yönet'i seçin.
NuGet Paket yöneticisi: SignalR.StockTicker'daGözat'ı seçin.
Paket kaynağı'ndannuget.org'yi seçin.
Arama kutusuna SignalR.Sample yazın ve Microsoft.AspNet.SignalR.Sample>Yükleme'yi seçin.
Çözüm Gezgini'daSignalR.Sample klasörünü genişletin.
SignalR.Sample paketinin yüklenmesi klasörü ve içeriğini oluşturdu.
SignalR.Sample klasöründe StockTicker.htmlöğesine sağ tıklayın ve Ardından Başlangıç Sayfası Olarak Ayarla'yı seçin.
Not
SignalR.Sample NuGet paketinin yüklenmesi Betikler klasörünüzde bulunan jQuery sürümünü değiştirebilir. Paketin SignalR.Sample klasörüne yüklemiş olduğu yeni StockTicker.html dosyası paketin yüklemiş olduğu jQuery sürümüyle eşitlenir, ancak özgün StockTicker.html dosyanızı yeniden çalıştırmak isterseniz, önce betik etiketindeki jQuery başvurusunu güncelleştirmeniz gerekebilir.
Uygulamayı çalıştırma
İlk uygulamada gördüğünüz tabloda yararlı özellikler vardı. Tam hisse senedi değerleyici uygulaması yeni özellikleri gösterir: hisse senedi verilerini ve hisse senetleri yükseldikçe ve düştükçe renk değiştiren hisse senetlerini gösteren yatay olarak kayan bir pencere.
Uygulamayı çalıştırmak için F5 tuşuna basın.
Uygulamayı ilk kez çalıştırdığınızda "market" "kapalıdır" ve statik bir tablo ve kaydırma olmayan bir değer çizgisi penceresi görürsünüz.
Açık Pazar'ı seçin.
Canlı Hisse Senedi DeğerLeyicisi kutusu yatay olarak kaydırmaya başlar ve sunucu hisse senedi fiyat değişikliklerini rastgele olarak düzenli aralıklarla yayınlamaya başlar.
Bir hisse senedi fiyatı her değiştiğinde, uygulama hem Live Stock Table'ı hem de Live Stock Ticker'ı güncelleştirir.
Bir hisse senedinin fiyat değişikliği pozitif olduğunda, uygulama hisse senedini yeşil bir arka planla gösterir.
Değişiklik negatif olduğunda, uygulama hisse senedini kırmızı arka plan ile gösterir.
Pazarı Kapat'ı seçin.
Tablo güncelleştirmeleri durduruluyor.
Değer çizgisi kaydırmayı durdurur.
Sıfırla’yı seçin.
Tüm hisse senedi verileri sıfırlanır.
Uygulama, fiyat değişiklikleri başlamadan önce ilk durumu geri yükler.
Tarayıcıdan URL'yi kopyalayın, iki tarayıcı daha açın ve URL'leri adres çubuklarına yapıştırın.
Her tarayıcıda aynı verilerin aynı anda dinamik olarak güncelleştirildiğini görürsünüz.
Denetimlerden herhangi birini seçtiğinizde, tüm tarayıcılar aynı anda aynı şekilde yanıt verir.
Canlı Hisse Senedi Değerleyici ekranı
Canlı Hisse Senedi Değerleyicisi ekranı, CSS stillerine göre tek bir <div>
satıra biçimlendirilmiş bir öğede sıralanmamış bir listedir. Uygulama, bir şablon dizesindeki <li>
yer tutucuları değiştirerek ve öğeleri öğeye dinamik olarak ekleyerek <li>
değerleyiciyi <ul>
tabloyla aynı şekilde başlatır ve güncelleştirir. Uygulama, içindeki sıralanmamış listenin sol kenar boşluğunu değiştirmek için jQuery animate
işlevini kullanarak kaydırmayı <div>
içerir.
SignalR.Sample StockTicker.html
Hisse senedi değerleyici HTML kodu:
<h2>Live Stock Ticker</h2>
<div id="stockTicker">
<div class="inner">
<ul>
<li class="loading">loading...</li>
</ul>
</div>
</div>
SignalR.Sample StockTicker.css
Hisse senedi değerleyici CSS kodu:
#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
Bunu kaydıran jQuery kodu:
function scrollTicker() {
var w = $stockTickerUl.width();
$stockTickerUl.css({ marginLeft: w });
$stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
}
sunucuda istemcinin çağırabileceği ek yöntemler
Uygulamaya esneklik eklemek için uygulamanın çağırabileceği ek yöntemler vardır.
SignalR.Sample StockTickerHub.cs
sınıfı, StockTickerHub
istemcinin çağırabileceği dört ek yöntem tanımlar:
public string GetMarketState()
{
return _stockTicker.MarketState.ToString();
}
public void OpenMarket()
{
_stockTicker.OpenMarket();
}
public void CloseMarket()
{
_stockTicker.CloseMarket();
}
public void Reset()
{
_stockTicker.Reset();
}
Uygulama, sayfanın üst kısmındaki düğmelere yanıt olarak , CloseMarket
ve Reset
çağrılarını OpenMarket
çağırır. Tüm istemcilere hemen yayılan durumda bir değişikliği tetikleyen bir istemcinin desenini gösterir. Bu yöntemlerin her biri, sınıfında pazar durumu değişikliğine StockTicker
neden olan ve ardından yeni durumu yayınlayan bir yöntem çağırır.
SignalR.Sample StockTicker.cs
StockTicker
sınıfında uygulama, enum değeri döndüren MarketState
bir MarketState
özellik ile pazarın durumunu korur:
public MarketState MarketState
{
get { return _marketState; }
private set { _marketState = value; }
}
public enum MarketState
{
Closed,
Open
}
Sınıfın iş parçacığı açısından güvenli olması gerekeceğinden, pazar durumunu değiştiren yöntemlerin her biri bunu bir kilit bloğu içinde StockTicker
yapar:
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();
}
}
Bu kodun iş parçacığı açısından güvenli olduğundan emin olmak için, _marketState
belirtilen volatile
özelliği destekleyen MarketState
alan:
private volatile MarketState _marketState;
BroadcastMarketStateChange
ve BroadcastMarketReset
yöntemleri, daha önce gördüğünüz BroadcastStockPrice yöntemine benzer, ancak istemcide tanımlanan farklı yöntemleri çağırır:
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();
}
İstemcide sunucunun çağırabileceği ek işlevler
İşlev updateStockPrice
artık hem tabloyu hem de değer çizgisi ekranını işler ve kırmızı ve yeşil renkleri yanıp sönmek için kullanır jQuery.Color
.
SignalR.StockTicker.js'daki yeni işlevler, pazar durumuna göre düğmeleri etkinleştirir ve devre dışı bırakır. Ayrıca Canlı Hisse Senedi DeğerLeyicisi yatay kaydırmayı durdurur veya başlatır. uygulamasına ticker.client
birçok işlev eklendiğinden, uygulama bunları eklemek için jQuery genişletme işlevini kullanır.
$.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();
}
});
Bağlantı kurulduktan sonra ek istemci kurulumu
İstemci bağlantıyı kurduktan sonra yapması gereken bazı ek işler vardır:
Uygun
marketOpened
marketClosed
veya işlevi çağırmak için pazarın açık mı yoksa kapalı mı olduğunu öğrenin.Düğmelere sunucu yöntemi çağrılarını ekleyin.
$.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();
});
});
Uygulama bağlantıyı kurana kadar sunucu yöntemleri düğmelere bağlı değildir. Böylece kod, kullanılabilir olmadan önce sunucu yöntemlerini çağıramaz.
Ek kaynaklar
Bu öğreticide, sunucudan tüm bağlı istemcilere ileti yayınlayan bir SignalR uygulamasının nasıl programlandığını öğrendiniz. Artık iletileri düzenli aralıklarla ve herhangi bir istemciden gelen bildirimlere yanıt olarak yayınlayabilirsiniz. Çok oyunculu çevrimiçi oyun senaryolarında sunucu durumunu korumak için çok iş parçacıklı tekil örnek kavramını kullanabilirsiniz. Bir örnek için SignalR tabanlı ShootR oyununa bakın.
Eşler arası iletişim senaryolarını gösteren öğreticiler için bkz . SignalR ile Çalışmaya Başlama ve SignalR ile Gerçek Zamanlı Güncelleştirme.
SignalR hakkında daha fazla bilgi için aşağıdaki kaynaklara bakın:
Sonraki adımlar
Bu öğreticide şunları yaptınız:
- Proje oluşturuldu
- Sunucu kodunu ayarlama
- Sunucu kodunu inceledim
- İstemci kodunu ayarlama
- İstemci kodunu inceledim
- Uygulamayı test etme
- Günlüğe kaydetme etkinleştirildi
ASP.NET SignalR 2 kullanan gerçek zamanlı bir web uygulaması oluşturmayı öğrenmek için sonraki makaleye ilerleyin.