Aracılığıyla paylaş


Öğretici: ASP.NET SignalR 1x ile Sunucu Yayını

Tarafından Patrick Fletcher, Tom Dykstra

Uyarı

Bu belgeler SignalR'nin en son sürümüne yönelik değildir. ASP.NET Core SignalR'ye göz atın.

Bu öğreticide, sunucu yayını işlevselliği sağlamak için ASP.NET SignalR kullanan bir web uygulamasının nasıl oluşturulacağı gösterilmektedir. Sunucu yayını, istemcilere gönderilen iletişimlerin sunucu tarafından başlatıldığı anlamına gelir. Bu senaryo, istemcilere gönderilen iletişimlerin bir veya daha fazla istemci tarafından başlatıldığı sohbet uygulamaları gibi eşler arası senaryolardan farklı bir programlama yaklaşımı gerektirir.

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.

Öğreticideki yorumlar kabul edilir. Öğreticiyle doğrudan ilgili olmayan sorularınız varsa bunları ASP.NET SignalR forumunu veya StackOverflow.com gönderebilirsiniz.

Genel Bakış

Microsoft.AspNet.SignalR.Sample NuGet paketi, Visual Studio projesine örnek bir sanal hisse senedi değerleyici uygulaması yükler. Bu öğreticinin ilk bölümünde, bu uygulamanın sıfırdan basitleştirilmiş bir sürümünü oluşturacaksınız. Öğreticinin geri kalanında NuGet paketini yükleyecek ve oluşturduğu ek özellikleri ve kodu gözden geçireceksiniz.

Hisse senedi değerleyici uygulaması, sunucudan tüm bağlı istemcilere düzenli aralıklarla "göndermek" veya yayınlamak istediğiniz gerçek zamanlı bir uygulamanın bir türünü temsil eder.

Bu öğreticinin ilk bölümünde oluşturabileceğiniz uygulama, hisse senedi verilerini içeren bir kılavuz görüntüler.

StockTicker ilk sürümü

Sunucu düzenli aralıklarla hisse senedi fiyatlarını rastgele güncelleştirir ve güncelleştirmeleri tüm bağlı istemcilere iletir. Tarayıcıda Değiştir ve sütunlarındaki 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, bunların tümü aynı verileri ve verilerde aynı değişiklikleri aynı anda gösterir.

Bu öğretici aşağıdaki bölümleri içerir:

Not

Uygulamayı oluşturma adımlarında çalışmak istemiyorsanız SignalR.Sample paketini yeni bir Boş ASP.NET Web Uygulaması projesine yükleyebilir ve kodun açıklamalarını almak için bu adımları okuyabilirsiniz. Öğreticinin ilk bölümü SignalR.Sample kodunun bir alt kümesini, ikinci bölümde ise SignalR.Sample paketindeki ek işlevlerin temel özellikleri açıklanmaktadır.

Önkoşullar

Başlamadan önce, bilgisayarınızda Visual Studio 2012 veya 2010 SP1'in yüklü olduğundan emin olun. Visual Studio'nuz yoksa ücretsiz Web için Visual Studio 2012 Express'i edinmek için bkz. İndirmeler ASP.NET.

Visual Studio 2010'larınız varsa NuGet'in yüklü olduğundan emin olun.

Proje oluşturma

  1. Dosyamenüsünden Yeni Proje'ye tıklayın.

  2. Yeni Proje iletişim kutusunda Şablonlar'ın altında C# öğesini genişletin ve Web'i seçin.

  3. Boş Web Uygulaması şablonunu ASP.NET seçin, projeyi SignalR.StockTicker olarak adlandırın ve Tamam'a tıklayın.

    Yeni Proje iletişim kutusu

SignalR NuGet Paketlerini ekleme

SignalR ve JQuery NuGet Paketlerini Ekleme

Bir NuGet paketi yükleyerek bir projeye SignalR işlevselliği ekleyebilirsiniz.

  1. Araçlar |'a tıklayın NuGet Paket Yöneticisi | Paket Yöneticisi Konsolu.

  2. Paket yöneticisine aşağıdaki komutu girin.

    Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
    

    SignalR paketi, bağımlılık olarak bir dizi diğer NuGet paketini yükler. Yükleme tamamlandığında, ASP.NET bir uygulamada SignalR kullanmak için gereken tüm sunucu ve istemci bileşenlerine sahip olursunuz.

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.

  1. Proje klasöründe yeni bir sınıf dosyası oluşturun, stock.cs olarak adlandırın ve şablon kodunu aşağıdaki 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 senetleri oluştururken ayarlayacağınız iki özellik Sembol (örneğin, Microsoft için MSFT) ve Price'dır. Diğer özellikler, Price'ı nasıl ve ne zaman ayarladığınıza bağlıdır. Price'ı ilk kez ayarladığınızda değer DayOpen'a yayılır. Price'ı ayarladığınızda, Change ve PercentChange özellik değerleri Price ve DayOpen arasındaki farka göre hesaplanır.

StockTicker ve StockTickerHub sınıflarını oluşturma

Sunucudan istemciye etkileşimi işlemek için SignalR Hub API'sini kullanacaksınız. SignalR Hub sınıfından türetilen bir StockTickerHub sınıfı, istemcilerden gelen bağlantıları ve yöntem çağrılarını almayı işler. Ayrıca, istemci bağlantılarından bağımsız olarak fiyat güncelleştirmelerini düzenli aralıklarla tetikleyebilmek için stok verilerini korumanız ve zamanlayıcı nesnesi çalıştırmanız gerekir. Hub örnekleri geçici olduğundan bu işlevleri Hub sınıfına koyamazsınız. Hub'da her işlem için istemciden sunucuya bağlantılar ve çağrılar gibi bir Hub sınıf örneği oluşturulur. Bu nedenle hisse senedi verilerini tutan, fiyatları güncelleştiren ve fiyat güncelleştirmelerini yayınlayan mekanizma ayrı bir sınıfta çalıştırılabildiğinden, StockTicker adını alırsınız.

StockTicker'dan yayın

StockTicker sınıfının yalnızca bir örneğinin sunucuda çalışmasını istiyorsunuz, bu nedenle her StockTickerHub örneğinden singleton StockTicker örneğine bir başvuru ayarlamanız gerekir. StockTicker sınıfının, hisse senedi verilerine sahip olduğundan ve güncelleştirmeleri tetiklediğinden istemcilere yayın yapabilmesi gerekir, ancak StockTicker bir Hub sınıfı değildir. Bu nedenle, StockTicker sınıfının 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.

  1. Çözüm Gezgini'da projeye sağ tıklayın ve Yeni Öğe Ekle'ye tıklayın.

  2. ASP.NET and Web Tools 2012.2 Güncelleştirmesine sahip Visual Studio 2012'niz varsa, Visual C# altında Web'e tıklayın ve SignalR Hub Sınıfı öğe şablonunu seçin. Aksi takdirde Sınıf şablonunu seçin.

  3. Yeni sınıfı StockTickerHub.cs olarak adlandırın ve Ekle'ye tıklayın.

    StockTickerHub.cs ekleme

  4. Şablon kodunu aşağıdaki kodla değiştirin:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    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();
            }
        }
    }
    

    Hub sınıfı, istemcilerin sunucuda çağırabileceği yöntemleri tanımlamak için kullanılır. Tek bir yöntem tanımlayabilirsiniz: 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 yürütülebilir 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 ne zaman yürütülür?

    HubName özniteliği, istemcideki JavaScript kodunda Hub'a nasıl başvurulacağını belirtir. Bu özniteliği kullanmazsanız istemcideki varsayılan ad, sınıf adının camel cased sürümüdür ve bu durumda stockTickerHub olacaktır.

    Daha sonra StockTicker sınıfını oluşturduğunuzda göreceğiniz gibi, bu sınıfın tekil bir örneği statik Instance özelliğinde oluşturulur. StockTicker'ın tekil örneği, kaç istemcinin bağlanmasına veya bağlantısının kesilmesine bakılmaksızın bellekte kalır ve GetAllStocks yönteminin geçerli hisse senedi bilgilerini döndürmek için kullandığı örnektir.

  5. Proje klasöründe yeni bir sınıf dosyası oluşturun, stockTicker.cs olarak adlandırın ve şablon kodunu aşağıdaki 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 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 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);
            }
    
        }
    }
    

    Birden çok iş parçacığı aynı StockTicker kodu örneğini çalıştıracağı için StockTicker sınıfının threadsafe olması gerekir.

    Tekil örneği statik alanda depolama

    Kod, Instance özelliğini sınıfın bir örneğiyle destekleyen statik _instance alanını başlatır ve oluşturucu özel olarak işaretlendiğinden, sınıfın oluşturulabilen tek örneği budur. Gecikmeli başlatma , performans nedenleriyle değil, örnek oluşturma işleminin iş parçacığı güvenli olduğundan emin olmak için _instance alanı için kullanılı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ışan yeni bir StockTickerHub sınıfı örneği, StockTickerHub sınıfında daha önce gördüğünüz gibi StockTicker.Instance statik özelliğinden StockTicker tekil örneğini alır.

    Hisse senedi verilerini ConcurrentDictionary'de depolama

    Oluşturucu, _stocks koleksiyonunu bazı örnek hisse senedi verileriyle başlatır ve GetAllStocks hisse senetlerini döndürür. Daha önce gördüğünüz gibi bu hisse senetleri koleksiyonu, istemcilerin çağırabileceği Hub sınıfında bir sunucu yöntemi olan StockTickerHub.GetAllStocks tarafından döndürülür.

    private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
    
    private StockTicker(IHubConnectionContext 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 StockTicker örneği atıldığında 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 Timer nesnesi başlatır.

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

    UpdateStockPrices, durum parametresinde null değerini geçiren Zamanlayıcı tarafından çağrılır. Fiyatları güncelleştirmeden önce, _updateStockPricesLock nesnesinde bir kilit alınır. Kod, başka bir iş parçacığının fiyatları zaten güncelleştirip güncelleştirmediğini denetler ve listedeki her hisse senedinde TryUpdateStockPrice öğesini çağırır. TryUpdateStockPrice yöntemi hisse senedi fiyatının değiştirilip değiştirilmeyeceğine ve ne kadar değiştirilmeyeceğine karar verir. Hisse senedi fiyatı değiştirilirse, hisse senedi fiyat değişikliğini tüm bağlı müşterilere yayınlamak için BroadcastStockPrice çağrılır.

    _updatingStockPrices bayrağı, erişimin iş parçacığı güvenli olduğundan emin olmak için geçici olarak işaretlenir.

    private volatile bool _updatingStockPrices = false;
    

    Gerçek bir uygulamada TryUpdateStockPrice yöntemi, fiyatı aramak için bir web hizmetini çağırır; bu kodda 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şikliklerinin kaynağı stockTicker nesnesi olduğundan, tüm bağlı istemcilerde updateStockPrice yöntemini çağırması gereken nesne budur. Hub sınıfında istemci yöntemlerini çağırmak için bir API'niz vardır, ancak StockTicker Hub sınıfından türetilmez ve herhangi bir Hub nesnesine başvurusu yoktur. Bu nedenle, bağlı istemcilere yayın yapmak için StockTicker sınıfının StockTickerHub sınıfı için SignalR bağlam örneğini alması ve istemcilerde yöntemleri çağırmak için bunu kullanması gerekir.

    Kod, tekli sınıf örneğini oluşturduğunda SignalR bağlamı için bir başvuru alır, oluşturucuya başvuruyu geçirir ve oluşturucu bunu İstemciler özelliğine yerleştirir.

    Bağlamı yalnızca bir kez almak istemenin iki nedeni vardır: bağlamı almak pahalı bir işlemdir ve bir kez almak, istemcilere gönderilen iletilerin hedeflenen sırasının korunmasını sağlar.

    private readonly static Lazy<StockTicker> _instance =
        new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
    
    private StockTicker(IHubConnectionContext clients)
    {
        Clients = clients;
    
        // Remainder of constructor ...
    }
    
    private IHubConnectionContext Clients
    {
        get;
        set;
    }
    
    private void BroadcastStockPrice(Stock stock)
    {
        Clients.All.updateStockPrice(stock);
    }
    

    Bağlamın Clients özelliğini alıp StockTickerClient özelliğine koymak, Hub sınıfında olduğu gibi görünen istemci yöntemlerini çağırmak için kod yazmanıza olanak tanır. Örneğin, tüm istemcilere yayınlamak için Clients.All.updateStockPrice(stock) yazabilirsiniz.

    BroadcastStockPrice içinde çağırdığınız updateStockPrice yöntemi henüz mevcut değil; daha sonra istemcide çalışan bir kod yazdığınızda ekleyeceksiniz. burada updateStockPrice'e başvurabilirsiniz çünkü Clients.All dinamiktir ve bu da ifadenin çalışma zamanında değerlendirileceğ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 updateStockPrice adlı bir yöntemi varsa, bu yöntem çağrılır ve parametre değeri bu yönteme geçirilir.

    Clients.All, tüm istemcilere göndermek 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 Global.asax dosyasına kod ekleyeceksiniz.

  1. Çözüm Gezgini'da projeye sağ tıklayın ve ardından Yeni Öğe Ekle'ye tıklayın.

  2. Genel Uygulama Sınıfı öğe şablonunu seçin ve ekle'ye tıklayın.

    global.asax ekleme

  3. SignalR yol kayıt kodunu Application_Start yöntemine ekleyin:

    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
    }
    

    Varsayılan olarak, tüm SignalR trafiğinin temel URL'si "/signalr" ve "/signalr/hubs" ise uygulamanızdaki tüm Hub'lar için proxy'leri tanımlayan dinamik olarak oluşturulmuş bir JavaScript dosyasını almak için kullanılır. MapHubs yöntemi, HubConfiguration sınıfının bir örneğinde farklı bir temel URL ve belirli SignalR seçeneklerini belirtmenize olanak sağlayan aşırı yüklemeler içerir.

  4. Dosyanın en üstüne bir using deyimi ekleyin:

    using System.Web.Routing;
    
  5. Global.asax dosyasını kaydedip kapatın ve projeyi derleyin.

Sunucu kodunu ayarlamayı tamamladınız. Sonraki bölümde istemciyi ayarlayacaksınız.

İstemci kodunu ayarlama

  1. Proje klasöründe yeni bir HTML dosyası oluşturun ve StockTicker.htmlolarak adlandırın.

  2. Şablon kodunu aşağıdaki 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.8.2.min.js" ></script>
        <!--Reference the SignalR library. -->
        <script src="/Scripts/jquery.signalR-1.0.1.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, 5 sütunlu bir tablo, üst bilgi satırı ve 5 sütunun tümüne yayılan tek bir hücre içeren bir veri satırı oluşturur. Veri satırında "yükleniyor..." görüntüleniyor ve yalnızca uygulama başlatıldığında kısa bir süre gösterilir. JavaScript kodu bu satırı kaldırır ve sunucudan alınan hisse senedi verileriyle yerine satır ekler.

    Betik etiketleri jQuery betik dosyasını, SignalR çekirdek betik dosyasını, SignalR proxy'leri betik dosyasını ve daha sonra oluşturacağınız stockTicker betik dosyasını belirtir. "/signalr/hubs" URL'sini belirten SignalR proxy'leri betik dosyası dinamik olarak oluşturulur ve Bu örnekte StockTickerHub.GetAllStocks için Hub sınıfındaki yöntemler için ara sunucu yöntemlerini tanımlar. İsterseniz SignalR Yardımcı Programlarını kullanarak bu JavaScript dosyasını el ile oluşturabilir ve MapHubs yöntem çağrısında dinamik dosya oluşturmayı devre dışı bırakabilirsiniz.

  3. Önemli

    StockTicker.html'daki JavaScript dosya başvurularının doğru olduğundan emin olun. Başka bir ifadeyle, betik etiketinizdeki jQuery sürümünün (örnekte 1.8.2) projenizin Scripts klasöründeki jQuery sürümüyle aynı olduğundan emin olun ve betik etiketinizdeki SignalR sürümünün projenizin Scripts klasöründeki SignalR sürümüyle aynı olduğundan emin olun. Gerekirse betik etiketlerindeki dosya adlarını değiştirin.

  4. Çözüm Gezgini'daStockTicker.htmlsağ tıklayın ve ardından Başlangıç Sayfası Olarak Ayarla'ya tıklayın.

  5. Proje klasöründe yeni bir JavaScript dosyası oluşturun ve StockTicker.jsolarak adlandırın..

  6. Şablon kodunu aşağıdaki kodla değiştirin:

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

    $.connection, SignalR proxy'lerini ifade eder. Kod, StockTickerHub sınıfının ara sunucusuna bir başvuru alır ve bunu ticker değişkenine yerleştirir. Proxy adı, [HubName] özniteliği tarafından ayarlanan addır:

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

    Tüm değişkenler ve işlevler tanımlandıktan sonra, dosyadaki son kod satırı SignalR start işlevini çağırarak SignalR bağlantısını başlatır. Start işlevi zaman uyumsuz olarak yürütülür ve jQuery Ertelenmiş nesnesini döndürür. Bu, zaman uyumsuz işlem tamamlandığında çağrılacak işlevi belirtmek için bitti işlevini çağırabileceğiniz anlamına gelir..

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

    init işlevi sunucuda getAllStocks işlevini çağırır ve hisse senedi tablosunu güncelleştirmek için sunucunun döndürdüğü bilgileri kullanır. Varsayılan olarak, istemcide deve büyük/küçük harf kullanımını kullanmanız gerekir, ancak yöntem adı sunucuda pascal-cased şeklindedir. Deve büyük/küçük harf kuralı nesnelere değil yalnızca yöntemlere uygulanır. Örneğin, hisse senedine başvurursunuz. Sembol ve stok. Fiyat, stock.symbol veya stock.price değil.

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

    İstemcide pascal büyük/küçük harf kullanımını kullanmak istiyorsanız veya tamamen farklı bir yöntem adı kullanmak istiyorsanız Hub yöntemini HubMethodName özniteliğiyle, Hub sınıfının kendisini HubName özniteliğiyle aynı şekilde süsleyebilirsiniz.

    init yönteminde, sunucudan alınan her hisse senedi nesnesi için, hisse senedi nesnesinin özelliklerini biçimlendirmek için formatStock çağrılır ve ardından rowTemplate değişkenindeki yer tutucuları hisse senedi nesnesi özellik değerleriyle değiştirmek için supplant ( StockTicker.jsen üstünde tanımlanır) çağrılarak tablo satırı için HTML oluşturulur. Sonuçta elde edilen HTML, hisse senedi tablosuna eklenir.

    Zaman uyumsuz başlangıç işlevi tamamlandıktan sonra yürütülen bir geri çağırma işlevi olarak geçirerek init'i çağırırsınız. start çağrısından sonra init'i ayrı bir JavaScript deyimi olarak çağırdıysanız, start işlevinin bağlantıyı kurmayı bitirmesini beklemeden hemen yürütüleceğinden işlev başarısız olur. Bu durumda, init işlevi sunucu bağlantısı kurulmadan önce getAllStocks işlevini çağırmaya çalışır.

    Sunucu hisse senedi fiyatını değiştirdiğinde, bağlı istemcilerde updateStockPrice'i çağırır. İşlev, sunucudan gelen çağrılar için kullanılabilir hale getirmek için stockTicker proxy'sinin istemci özelliğine eklenir.

    ticker.client.updateStockPrice = function (stock) {
        var displayStock = formatStock(stock),
            $row = $(rowTemplate.supplant(displayStock));
    
        $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
            .replaceWith($row);
        }
    

    updateStockPrice işlevi, sunucudan alınan hisse senedi nesnesini init işlevindeki gibi bir tablo satırına biçimlendirir. Ancak satırı tabloya eklemek yerine, hisse senedinin tablodaki geçerli satırını bulur ve bu satırı yenisiyle değiştirir.

Uygulamayı test edin

  1. Uygulamayı hata ayıklama modunda çalıştırmak için F5 tuşuna basın.

    Hisse senedi tablosunda başlangıçta "yükleniyor..." ifadesi görüntülenir satırına gidin ve kısa bir gecikmeden sonra ilk hisse senedi verileri görüntülenir ve hisse senedi fiyatları değişmeye başlar.

    Yükleme

    İlk hisse senedi tablosu

    Sunucudan değişiklik alan hisse senedi tablosu

  2. URL'yi tarayıcı adres çubuğundan kopyalayın ve bir veya daha fazla yeni tarayıcı penceresine yapıştırın.

    İlk stok ekranı ilk tarayıcıyla aynıdır ve değişiklikler aynı anda gerçekleşir.

  3. Tüm tarayıcıları kapatın ve yeni bir tarayıcı açın, ardından aynı URL'ye gidin.

    StockTicker tekil nesnesi sunucuda çalışmaya devam ettiğinden hisse senedi tablosu, hisse senetlerinin değişmeye devam ettiğini gösterir. (Sıfır değişiklik rakamı içeren ilk tabloyu görmezsiniz.)

  4. Tarayıcıyı kapatın.

Günlü kaydını etkinleştir

SignalR,sorun gidermeye yardımcı olmak 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ştirir ve günlüklerin SignalR'nin aşağıdaki aktarım yöntemlerinden hangilerini kullandığını nasıl gösterdiğini gösteren örnekleri görürsünüz:

Belirli bir bağlantı için SignalR, hem sunucunun hem de istemcinin desteklediği en iyi aktarım yöntemini seçer.

  1. StockTicker.js açın ve dosyanın sonunda bağlantıyı başlatan koddan hemen önce günlüğe kaydetmeyi etkinleştirmek için bir kod satırı ekleyin:

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  2. Projeyi çalıştırmak için F5 tuşuna basın.

  3. Tarayıcınızın geliştirici araçları penceresini açın ve günlükleri görmek için Konsol'u seçin. Signalr'ın yeni bir bağlantı için taşıma yönteminde anlaşmaya varan günlüklerini görmek için sayfayı yenilemeniz gerekebilir.

    Internet Explorer 10'Windows 8 (IIS 8) üzerinde çalıştırıyorsanız, aktarım yöntemi WebSockets'tir.

    IE 10 IIS 8 Konsolu

    Windows 7'de (IIS 7.5) Internet Explorer 10 çalıştırıyorsanız, aktarım yöntemi iframe'dir.

    IE 10 Konsolu, IIS 7.5

    Bir Konsol penceresi almak için Firefox'ta Firebug eklentisini yükleyin. firefox 19'ı Windows 8 (IIS 8) üzerinde çalıştırıyorsanız, aktarım yöntemi WebSockets'tir.

    Firefox 19 IIS 8 Web Yuvaları

    Windows 7'de (IIS 7.5) Firefox 19 çalıştırıyorsanız, aktarım yöntemi sunucu tarafından gönderilen olaylardır.

    Firefox 19 IIS 7.5 Konsolu

StockTicker örneğinin tamamını yükleme ve gözden geçirme

Microsoft.AspNet.SignalR.Sample NuGet paketi tarafından yüklenen StockTicker uygulaması, 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ükleyecek ve yeni özellikleri ve bunları uygulayan kodu gözden geçireceksiniz.

SignalR.Sample NuGet paketini yükleme

  1. Çözüm Gezgini'da projeye sağ tıklayın ve NuGet Paketlerini Yönet'e tıklayın.

  2. NuGet Paketlerini Yönet iletişim kutusunda Çevrimiçi'ne tıklayın, Çevrimiçi Ara kutusuna SignalR.Sample yazın ve SignalR.Sample paketinde Yükle'ye tıklayın.

    SignalR.Sample paketini yükleme

  3. Global.asax dosyasında RouteTable.Routes.MapHubs() açıklama satırı yapın; daha önce Application_Start yöntemine eklediğiniz satır.

    SignalR.Sample paketi SignalR yolunu App_Start/RegisterHubs.cs dosyasına kaydettirdiğinden Global.asax içindeki kod artık gerekli değildir:

    [assembly: WebActivator.PreApplicationStartMethod(typeof(SignalR.StockTicker.RegisterHubs), "Start")]
    
    namespace SignalR.StockTicker
    {
        public static class RegisterHubs
        {
            public static void Start()
            {
                // Register the default hubs route: ~/signalr/hubs
                RouteTable.Routes.MapHubs();
            }
        }
    }
    

    Derleme özniteliği tarafından başvurulan WebActivator sınıfı, SignalR.Sample paketinin bağımlılığı olarak yüklenen WebActivatorEx NuGet paketine dahil edilir.

  4. Çözüm Gezgini'daSignalR.Sample paketi yüklenerek oluşturulan SignalR.Sample klasörünü genişletin.

  5. SignalR.Sample klasöründe StockTicker.htmlsağ tıklayın ve ardından Başlangıç Sayfası Olarak Ayarla'ya tıklayın.

    Not

    SignalR.Sample NuGet paketini yüklemek 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ükleyebileceği 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

  1. Uygulamayı çalıştırmak için F5'e basın.

    Daha önce gördüğünüz kılavuza ek olarak, tam hisse senedi değer çizgisi uygulaması aynı hisse senedi verilerini görüntüleyen yatay olarak kayan bir pencere gösterir. Uygulamayı ilk kez çalıştırdığınızda " pazar" "kapalıdır" ve kaydırma olmayan statik bir kılavuz ve bir değer çizgisi penceresi görürsünüz.

    StockTicker ekran başlangıcı

    Açık Piyasa'ya tıkladığınızda, 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. Hisse senedi fiyatı her değiştiğinde, hem Live Stock Table kılavuzu hem de Live Stock Ticker kutusu güncelleştirilir. Hisse senedinin fiyat değişikliği pozitif olduğunda, hisse senedi yeşil bir arka planla gösterilir ve değişiklik negatif olduğunda hisse senedi kırmızı arka plan ile gösterilir.

    StockTicker uygulaması, pazar açık

    Piyasayı Kapat düğmesi değişiklikleri durdurur ve değer çizgisi kaydırmayı durdurur ve Sıfırla düğmesi fiyat değişiklikleri başlamadan önce tüm hisse senedi verilerini ilk duruma sıfırlar. Daha fazla tarayıcı penceresi açar ve aynı URL'ye giderseniz, her tarayıcıda aynı anda aynı verilerin dinamik olarak güncelleştirildiğini görürsünüz. Düğmelerden birine tıkladığınızda, tüm tarayıcılar aynı anda aynı şekilde yanıt verir.

Canlı Hisse Senedi DeğerLeyicisi ekranı

Live Stock Ticker ekranı, CSS stilleri tarafından tek bir satırda biçimlendirilmiş bir div öğesinde sıralanmamış bir listedir. Değer çizgisi, tabloyla aynı şekilde başlatılır ve güncelleştirilir: li şablon dizesindeki <yer tutucuları değiştirerek ve li> öğelerini <ul> öğesine dinamik olarak ekleyerek<.> Kaydırma işlemi, div içindeki sıralanmamış listenin sol kenar boşluğunu değiştirmek için jQuery animasyon işlevi kullanılarak gerçekleştirilir.

Hisse senedi değerleyici HTML'i:

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

Hisse senedi değerleyici CSS:

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

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

StockTickerHub sınıfı, 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();
}

Sayfanın üst kısmındaki düğmelere yanıt olarak OpenMarket, CloseMarket ve Reset çağrılır. Tüm istemcilere hemen yayılan bir durum değişikliğini tetikleyen bir istemcinin desenini gösterir. Bu yöntemlerin her biri StockTicker sınıfında piyasa durumu değişikliğini etkileyen bir yöntem çağırır ve ardından yeni durumu yayınlar.

StockTicker sınıfında, pazarın durumu, MarketState sabit listesi değeri döndüren bir MarketState özelliği tarafından korunur:

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

public enum MarketState
{
    Closed,
    Open
}

StockTicker sınıfının threadsafe olması gerektiğinden, pazar durumunu değiştiren yöntemlerin her biri bunu bir kilit bloğu içinde 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 threadsafe olduğundan emin olmak için MarketState özelliğini destekleyen _marketState alanı geçici olarak işaretlenir.

private volatile MarketState _marketState;

BroadcastMarketStateChange ve BroadcastMarketReset yöntemleri, istemcide tanımlanan farklı yöntemleri çağırmaları dışında daha önce gördüğünüz BroadcastStockPrice yöntemine benzer:

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

updateStockPrice işlevi artık hem kılavuz hem de değer çizgisi ekranını işler ve kırmızı ve yeşil renkleri yanıp sönmek için jQuery.Color kullanır.

SignalR.StockTicker.js'deki yeni işlevler, pazar durumuna göre düğmeleri etkinleştirir ve devre dışı bırakır ve yatay kaydırmayı durdurur veya değer çizgisi penceresini başlatır. ticker.client dosyasına birden çok işlev eklendiğinden, bunları eklemek için jQuery genişletme işlevi kullanılı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 veya marketClosed işlevini çağırmak için pazarın açık mı yoksa kapalı mı olduğunu öğrenin ve sunucu yöntemi çağrılarını düğmelere 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();
        });
    });

Bağlantı kurulana kadar sunucu yöntemleri düğmelere bağlı değildir, böylece kod kullanılabilir olmadan önce sunucu yöntemlerini çağırmayı deneyemez.

Sonraki adımlar

Bu öğreticide, hem düzenli aralıklarla hem de herhangi bir istemciden gelen bildirimlere yanıt olarak sunucudan tüm bağlı istemcilere ileti yayınlayan bir SignalR uygulaması programlamayı öğrendiniz. Sunucu durumunu korumak için çok iş parçacıklı tekil örnek kullanma deseni, çok oyunculu çevrimiçi oyun senaryolarında da kullanılabilir. Bir örnek için SignalR'yi temel alan ShootR oyununa bakın.

Eşler arası iletişim senaryolarını gösteren öğreticiler için bkz . SignalR kullanmaya başlama ve SignalR ile Gerçek Zamanlı Güncelleştirme.

Daha gelişmiş SignalR geliştirme kavramları hakkında daha fazla bilgi edinmek için SignalR kaynak kodu ve kaynakları için aşağıdaki siteleri ziyaret edin: