溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

發(fā)布時(shí)間:2022-05-30 11:10:14 來(lái)源:億速云 閱讀:257 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    一、概述

    這篇教程通過實(shí)現(xiàn)一個(gè)股票報(bào)價(jià)的小程序來(lái)講解如何使用SignalR進(jìn)行服務(wù)器端的推送,服務(wù)器會(huì)模擬股票價(jià)格的波動(dòng),并把最新的股票價(jià)格推送給所有連接的客戶端,最終的運(yùn)行效果如下圖所示。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    教程:使用 SignalR 2 廣播的服務(wù)器

    可以通過Install-Package Microsoft.AspNet.SignalR.Sample來(lái)安裝并查看完整的代碼。

    二、服務(wù)器端代碼

    新建一個(gè)名為Stock.cs的實(shí)體類,用來(lái)作為服務(wù)器端推送消息的載體,具體代碼如下。

    這個(gè)實(shí)體類只有Symbol和Price這兩個(gè)屬性需要設(shè)置,其它屬性將會(huì)依據(jù)Price自動(dòng)進(jìn)行計(jì)算。

    using System;
    
    namespace Microsoft.AspNet.SignalR.StockTicker
    {
        public class Stock
        {
            private decimal _price;
    
            public string Symbol { get; set; }
    
            public decimal DayOpen { get; private set; }
    
            public decimal DayLow { get; private set; }
    
            public decimal DayHigh { get; private set; }
    
            public decimal LastChange { get; private set; }
    
            public decimal Price
            {
                get
                {
                    return _price;
                }
                set
                {
                    if (_price == value)
                    {
                        return;
                    }
    
                    LastChange = value - _price;
                    _price = value;
    
                    if (DayOpen == 0)
                    {
                        DayOpen = _price;
                    }
                    if (_price < DayLow || DayLow == 0)
                    {
                        DayLow = _price;
                    }
                    if (_price > DayHigh)
                    {
                        DayHigh = _price;
                    }
                }
            }
    
            public decimal Change
            {
                get
                {
                    return Price - DayOpen;
                }
            }
    
            public double PercentChange
            {
                get
                {
                    return (double)Math.Round(Change / Price, 4);
                }
            }
        }
    }

    1、創(chuàng)建StockTicker和StockTickerHub類

    我們將使用SignalR Hub API來(lái)處理服務(wù)器到客戶端的交互,所以新建一個(gè)繼承自SignalR Hub類的StockTickerHub類來(lái)處理客戶端的連接及調(diào)用。除此之外,我們還需要維護(hù)股票的價(jià)格數(shù)據(jù)以及新建一個(gè)Timer對(duì)象來(lái)定期的更新價(jià)格,而這些都與客戶端連接無(wú)關(guān)的。由于Hub實(shí)例的生命周期很短暫,只有在比如客戶端連接和調(diào)用的時(shí)候,Hub類實(shí)例是為Hub上的每個(gè)這樣的操作才會(huì)創(chuàng)建新的實(shí)例,所以不要把與客戶端連接及調(diào)用無(wú)關(guān)的代碼放置到SignalR Hub類中。在這里,我們將維護(hù)股票數(shù)據(jù)、模擬更新股票價(jià)格以及向客戶端推送股票價(jià)格的代碼放置到一個(gè)名為StockTicker的類中。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    我們只需要在服務(wù)器端運(yùn)行一個(gè)StockTicker類的實(shí)例(單例模式),由于這個(gè)StockTicker類維護(hù)著股票的價(jià)格,所以它也要能夠?qū)⒆钚碌墓善眱r(jià)格推送給所有的客戶端。為了達(dá)到這個(gè)目的,我們需要在這單個(gè)實(shí)例中引用所有的StockTickerHub實(shí)例,而這可以通過SignalR Hub的Context對(duì)象來(lái)獲得,然后它可以使用SignalR連接Context對(duì)象向客戶端廣播。

    2、StockTickerHub類

    這個(gè)Hub類用來(lái)定義客戶端可以調(diào)用的服務(wù)端方法,當(dāng)客戶端與服務(wù)器建立連接后,將會(huì)調(diào)用GetAllStocks()方法來(lái)獲得股票數(shù)據(jù)以及當(dāng)前的價(jià)格,因?yàn)檫@個(gè)方法是直接從內(nèi)存中讀取數(shù)據(jù)的,所以會(huì)立即返回IEnumerable數(shù)據(jù)。如果這個(gè)方法是通過其它可能會(huì)有延時(shí)的方式來(lái)調(diào)用最新的股票數(shù)據(jù)的話,比如從數(shù)據(jù)庫(kù)查詢,或者調(diào)用第三方的Web Service,那么就需要指定Task>來(lái)作為返回值,從而實(shí)現(xiàn)異步通信,更多信息請(qǐng)參考ASP.NET SignalR Hubs API Guide - Server - When to execute asynchronously。

    HubName屬性指定了該Hub的別名,即客戶端腳本調(diào)用的Hub名,如果不使用HubName屬性指定別名的話,默認(rèn)將會(huì)使用駱駝命名法,那么它在客戶端調(diào)用的名稱將會(huì)是stockTickerHub。

    using Microsoft.AspNet.SignalR.Hubs;
    using System.Collections.Generic;
    
    namespace Microsoft.AspNet.SignalR.StockTicker
    {
        [HubName("stockTicker")]
        public class StockTickerHub : Hub
        {
            private readonly StockTicker _stockTicker;
    
            public StockTickerHub()
    
                : this(StockTicker.Instance)
            {
            }
    
            public StockTickerHub(StockTicker stockTicker)
            {
                _stockTicker = stockTicker;
            }
    
            public IEnumerable GetAllStocks()
            {
                return _stockTicker.GetAllStocks();
            }
    
        }
    }

    接下來(lái)我們將會(huì)創(chuàng)建StockTicker類,并且創(chuàng)建一個(gè)靜態(tài)實(shí)例屬性。這樣不管有多少個(gè)客戶端連接或者斷開,內(nèi)存中都只有一個(gè)StockTicker類的實(shí)例,并且還可以通過該實(shí)例的GetAllStocks方法來(lái)獲得當(dāng)前的股票數(shù)據(jù)。

    3、StockTicker類

    由于所有線程都運(yùn)行 StockTicker 代碼的同一個(gè)實(shí)例,StockTicker 類必須要是線程安全的。

    1、將單個(gè)實(shí)例保存在一個(gè)靜態(tài)字段中

    在這個(gè)類中,我們新建了一個(gè)名為_instance的字段用來(lái)存放該類的實(shí)例,并且將構(gòu)造函數(shù)的訪問權(quán)限設(shè)置成私有狀態(tài),這樣其它的類就只能通過Instance這個(gè)靜態(tài)屬性來(lái)獲得該類的實(shí)例,而無(wú)法通過關(guān)鍵字new來(lái)創(chuàng)建一個(gè)新的實(shí)例。在這個(gè)_instance字段上面,我們使用了Lazy特性,雖然會(huì)損失一點(diǎn)兒性能,但是它卻可以保證以線程安全的方式來(lái)創(chuàng)建實(shí)例。

    每次客戶端連接到服務(wù)器時(shí),運(yùn)行在單獨(dú)線程中的StockTickerHub類的新實(shí)例都會(huì)從StockTicker獲取StockTicker單例實(shí)例。實(shí)例靜態(tài)屬性,如前面在StockTickerHub類中看到的。

    2、使用ConcurrentDictionary來(lái)存放股票數(shù)據(jù)

    這個(gè)類定義了一個(gè)_stocks字段來(lái)存放測(cè)試用的股票數(shù)據(jù),并且通過GetAllStocks這個(gè)方法來(lái)進(jìn)行獲取。我們前面講過客戶端會(huì)通過StockTickerHub.GetAllStocks來(lái)獲取當(dāng)前的股票數(shù)據(jù),其實(shí)就是這里的股票數(shù)據(jù)。

    在這個(gè)測(cè)試程序中,我們將數(shù)據(jù)存直接存放在內(nèi)存中,這樣做并沒有什么問題,但在實(shí)際的應(yīng)用場(chǎng)景中,則需要將數(shù)據(jù)存放在數(shù)據(jù)庫(kù)之類的文件中以便長(zhǎng)久的保存。

    3、定期的更新股票價(jià)格

    在這個(gè)類中,我們定義了一個(gè)Timer對(duì)象來(lái)定期的更新股票的價(jià)格。

    在實(shí)際的場(chǎng)景中,TryUpdateStockPrice方法通常會(huì)通過調(diào)用第三方的Web Service來(lái)獲取最新的股票價(jià)格,而在這個(gè)程序中,我們則是通過隨機(jī)數(shù)來(lái)進(jìn)行模擬該實(shí)現(xiàn)。

    4、通過SignalR Hub的Context對(duì)象來(lái)實(shí)現(xiàn)服務(wù)端的推送

    因?yàn)楣善眱r(jià)格變動(dòng)是在StockTicker對(duì)象中,所以這個(gè)對(duì)象需要調(diào)用客戶端的updateStockPrice回調(diào)方法來(lái)推送數(shù)據(jù)。在Hub類中,我們可以直接使用API來(lái)調(diào)用客戶端的方法,但是這個(gè)StockTicker類并沒有繼承自Hub,所以無(wú)法直接使用這些對(duì)象。為了能夠向客戶端廣播數(shù)據(jù),StockTicker類需要使用SignalR Hub的Context對(duì)象來(lái)獲得StokTickerHub類的實(shí)例,并用它來(lái)調(diào)用客戶端的方法。

    using Microsoft.AspNet.SignalR.Hubs;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading;
    
    namespace Microsoft.AspNet.SignalR.StockTicker
    {
        public class StockTicker
        {
            #region 字段
    
            // 初始化StockTicker為單例實(shí)例
            private readonly static Lazy _instance = new Lazy(() => new StockTicker(  GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients) );
             //在創(chuàng)建StockTicker類靜態(tài)實(shí)例的時(shí)候,把SignalR的Context引用通過構(gòu)造函數(shù)傳遞給Clients這個(gè)屬性。
             //在這里只需要獲取一次SignalR.Context,這樣做有2個(gè)好處,首先是因?yàn)楂@取SignalR.Context很耗費(fèi)資源,其次是獲取一次SignalR.Context可以保留消息發(fā)送到客戶端的預(yù)定義順序。
    
            private readonly object _marketStateLock = new object();
            private readonly object _updateStockPricesLock = new object();
    
            //為了線程安全,我們使用了ConcurrentDictionary來(lái)存放股票數(shù)據(jù),當(dāng)然你也可以使用Dictionary對(duì)象來(lái)進(jìn)行存儲(chǔ),但是在更新數(shù)據(jù)之前需要進(jìn)行鎖定。
            private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
    
            // 控制股票價(jià)格波動(dòng)的百分比
            private readonly double _rangePercent = 0.002;
    
            private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
            private readonly Random _updateOrNotRandom = new Random();
    
            private Timer _timer;
    
            //使用volatile修飾符來(lái)標(biāo)記_updatingStockPrices變量,該修飾符指示一個(gè)字段可以由多個(gè)同時(shí)執(zhí)行的線程修改,聲明為volatile的字段不受編譯器優(yōu)化(假定由單個(gè)線程訪問)的限制,這樣可以確保該字段在任何時(shí)間呈現(xiàn)的都是最新的值。
            //該修飾符通常用于由多個(gè)線程訪問但不使用lock語(yǔ)句對(duì)訪問進(jìn)行序列化的字段。
            private volatile bool _updatingStockPrices;
    
            #endregion 字段
    
            #region 構(gòu)造函數(shù)
    
            private StockTicker(IHubConnectionContext<dynamic> clients)
            {
                Clients = clients;
    
                _stocks.Clear();
    
                var stocks = new List
                {
                    new Stock { Symbol = "MSFT", Price = 41.68m },
                    new Stock { Symbol = "AAPL", Price = 92.08m },
                    new Stock { Symbol = "GOOG", Price = 543.01m }
                };
    
                stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));
    
                _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);//定時(shí)更新股價(jià)
            }
    
            #endregion 構(gòu)造函數(shù)
    
            #region 屬性
    
            private IHubConnectionContext<dynamic> Clients { get; set; } //使用Clients屬性,可以使您和在Hub類中一樣,通過它來(lái)調(diào)用客戶端的方法。
    
            public static StockTicker Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
    
            #endregion 屬性
    
            #region 方法
    
            //獲取所有股價(jià)
            public IEnumerable GetAllStocks()
            {
                return _stocks.Values;
            }
    
            //在更新之前,我們使用了_updateStockPricesLock對(duì)象將需要更新的部份進(jìn)行鎖定,并通過_updatingStockPrices變量來(lái)確定是否有其它線程已經(jīng)更新了股票的價(jià)格。
            //然后通過對(duì)每一個(gè)股票代碼執(zhí)行TryUpdateStockPrice方法來(lái)確定是否更新股票價(jià)格以及股票價(jià)格的波動(dòng)幅度。如果檢測(cè)到股票價(jià)格變動(dòng),將會(huì)通過BroadcastStockPrice方法將最新的股票價(jià)格推送給每一個(gè)連接的客戶端。
            private void UpdateStockPrices(object state)
            {
                // 此函數(shù)必須可重入re-entrant,因?yàn)樗亲鳛橛?jì)時(shí)器間隔處理程序運(yùn)行的。
                lock (_updateStockPricesLock)
                {
                    if (!_updatingStockPrices)
                    {
                        _updatingStockPrices = true;
    
                        foreach (var stock in _stocks.Values)
                        {
                            if (TryUpdateStockPrice(stock))
                            {
                                BroadcastStockPrice(stock);
                            }
                        }
    
                        _updatingStockPrices = false;
                    }
                }
            }
    
            private bool TryUpdateStockPrice(Stock stock)
            {
                // 隨機(jī)選擇是否更新該股票
                var r = _updateOrNotRandom.NextDouble();
                if (r > 0.1)
                {
                    return false;
                }
    
                // 以范圍百分比的隨機(jī)因子更新股票價(jià)格
                var random = new Random((int)Math.Floor(stock.Price));
                var percentChange = random.NextDouble() * _rangePercent;
                var pos = random.NextDouble() > 0.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); //Clients.All是dynamic類型的,意味著發(fā)送給所有的客戶端,同時(shí)SignalR還提供了用來(lái)指定具體的客戶端或組的屬性,具體信息可以參考HubConnectionContext。
            }
    
            #endregion 方法
        }
    }

    4、注冊(cè)SignalR路由

    服務(wù)器需要知道把哪些請(qǐng)求交由SignalR進(jìn)行操作,為了實(shí)現(xiàn)這個(gè)功能,我們需要在OWIN的Startup文件中進(jìn)行相應(yīng)的設(shè)置。

    using Owin;
    
    namespace Microsoft.AspNet.SignalR.StockTicker
    {
        public static class Startup
        {
            public static void ConfigureSignalR(IAppBuilder app)
            {
                // For more information on how to configure your application using OWIN startup, visit http://go.microsoft.com/fwlink/?LinkID=316888
    
                app.MapSignalR();
            }
        }
    }

    三、客戶端代碼

    StockTicker.html

    頁(yè)面分別引入了jQuery、SignalR、SignalR代理,以及StockTicker腳本文件。SignalR代理文件(/signalr/hubs)將會(huì)根據(jù)服務(wù)器端編寫的Hub文件動(dòng)態(tài)的生成相應(yīng)的腳本(生成關(guān)于StockTickerHub.GetAllStocks的相關(guān)代碼),如果你愿意,你還可以通過SignalR Utilities來(lái)手動(dòng)生成腳本文件,但是需要在MapHubs方法中禁用動(dòng)態(tài)文件創(chuàng)建的功能。

    注意:請(qǐng)確保StockTicker.html文件中引入的腳本文件在你的項(xiàng)目中是實(shí)際存在的。

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>ASP.NET SignalR Stock Ticker</title>
        <link href="StockTicker.css" rel="external nofollow"  rel="stylesheet" />
    </head>
    <body>
        <h2>ASP.NET SignalR Stock Ticker Sample</h2>
    
    
        <h3>Live Stock Table</h3>
        <div id="stockTable">
            <table border="1">
                <thead>
                    <tr><th>Symbol</th><th>Price</th><th>Open</th><th>High</th><th>Low</th><th>Change</th><th>%</th></tr>
                </thead>
                <tbody>
                    <tr class="loading"><td colspan="7">loading...</td></tr>
                </tbody>
            </table>
        </div>
    
        <h3>Live Stock Ticker</h3>
        <div id="stockTicker">
            <div class="inner">
                <ul>
                    <li class="loading">loading...</li>
                </ul> 
            </div>
        </div>
        <script src="jquery-1.10.2.min.js"></script>
        <script src="jquery.color-2.1.2.min.js"></script>
        <script src="../Scripts/jquery.signalR-2.4.1.js"></script>
        <script src="../signalr/hubs"></script>
        <script src="SignalR.StockTicker.js"></script>
    </body>
    </html>

    StockTicker.js

    // Crockford's supplant method (poor man's templating)自定義的模板方法
    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;
                }
            );
        };
    }
    
    // A simple background color flash effect that uses jQuery Color plugin
    jQuery.fn.flash = function (color, duration) {
        var current = this.css('backgroundColor');
        this.animate({ backgroundColor: 'rgb(' + color + ')' }, duration / 2)
            .animate({ backgroundColor: current }, duration / 2);
    };
    
    $(function () {
    
        var ticker = $.connection.stockTicker, //$.connection即是指SignalR代理,這行代碼表示將StockTickerHub類的代理的引用保存在變量ticker中,代理的名稱即為服務(wù)器端通過[HubName]屬性設(shè)置的名稱。
            up = '▲',
            down = '▼',
            $stockTable = $('#stockTable'),
            $stockTableBody = $stockTable.find('tbody'),
            rowTemplate = '{Symbol}{Price}{DayOpen}{DayHigh}{DayLow}{Direction} {Change}{PercentChange}',
            $stockTicker = $('#stockTicker'),
            $stockTickerUl = $stockTicker.find('ul'),
            liTemplate = '
    {Symbol} {Price} {Direction} {Change} ({PercentChange})
    ';
    
        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,
                DirectionClass: stock.Change === 0 ? 'even' : stock.Change >= 0 ? 'up' : 'down'
            });
        }
    
        function scrollTicker() {
            var w = $stockTickerUl.width();
            $stockTickerUl.css({ marginLeft: w });
            $stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
        }
    
        function stopTicker() {
            $stockTickerUl.stop();
        }
    
        //init方法調(diào)用服務(wù)端的getAllStocks方法,并將返回的數(shù)據(jù)顯示在Table中。服務(wù)端我們默認(rèn)會(huì)使用帕斯卡命名法,而SignalR會(huì)在生成客戶端的代理類時(shí),自動(dòng)將服務(wù)端的方法改成駱駝命名法,不過該規(guī)則只對(duì)方法名及Hub名稱有效。
        //而對(duì)于對(duì)象的屬性名,則仍然和服務(wù)器端的一樣,比如stock.Symbol、stock.Price,而不是stock.symbol、stock.price。
        //如果你想在客戶端使用與服務(wù)器商相同的名稱(包括大小寫),或者想自己定義其它的名稱,那么你可以通過給Hub方法加上HubMethodName標(biāo)簽來(lái)實(shí)現(xiàn)這個(gè)功能,而HubName標(biāo)簽則可以實(shí)現(xiàn)自定義的Hub名稱。
        function init() {
            return ticker.server.getAllStocks().done(function (stocks) {
                $stockTableBody.empty();
                $stockTickerUl.empty();
                //遍歷服務(wù)端返回的股票數(shù)據(jù),然后通過調(diào)用formatStock來(lái)格式化成我們想要的格式,接著通過supplant方法(在StockTicker.js的最頂端)來(lái)生成一條新行,并把這個(gè)新行插入到表格里面。
                $.each(stocks, function () {
                    var stock = formatStock(this);
                    $stockTableBody.append(rowTemplate.supplant(stock));                 
                    $stockTickerUl.append(liTemplate.supplant(stock));
                });
            });
        }
    
        //為了讓服務(wù)器能夠調(diào)用客戶的代碼,我們需要把updateStockPrice添加到stockTicker代理的client對(duì)象中
        $.extend(ticker.client, {
            updateStockPrice: function (stock) {
                var displayStock = formatStock(stock),
                    $row = $(rowTemplate.supplant(displayStock)),
                    $li = $(liTemplate.supplant(displayStock)),
                    bg = stock.LastChange < 0
                            ? '255,148,148' // red
                            : '154,240,117'; // green
                //該updateStockPrice方法和init方法一樣,通過調(diào)用formatStock來(lái)格式化成我們想要的格式,接著通過supplant方法(在StockTicker.js的最頂端)來(lái)生成一條新行,不過它并不是將該新行追加到Table中,而是找到Table中現(xiàn)有的行,然后使用新行替換它。
                $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);
                scrollTicker();
            }
        });
    
        // 客戶端的代碼編寫好之后,就可以通過最后的這行代碼來(lái)與服務(wù)器建立連接,由于這個(gè)start方法執(zhí)行的是異步操作,并會(huì)返回一個(gè)jQuery延時(shí)對(duì)象,所以我們要使用jQuery.done函數(shù)來(lái)處理連接成功之后的操作。
        //這個(gè)init方法其實(shí)是在start方法完成異步操作后作為回調(diào)函數(shù)執(zhí)行的,如果你把init作為一個(gè)獨(dú)立的JavaScript語(yǔ)句放在start方法之后的話,那么程序?qū)?huì)出錯(cuò),因?yàn)檫@樣會(huì)導(dǎo)致服務(wù)端的方法在客戶端還沒有與服務(wù)器建立連接之前就被調(diào)用。
        $.connection.hub.start().done(init);
    
    });

    四、輸出日志

    SignalR內(nèi)置了日志功能,你可以在客戶端選擇開啟該功能來(lái)幫助你調(diào)試程序,接下來(lái)我們將會(huì)通過開啟SignalR的日志功能來(lái)展示一下在不同的環(huán)境下SignalR所使用的傳輸技術(shù),大至總結(jié)如下:

    • WebSocket,至少需要IIS8及以上版本的支持。

    • Server-sent events,支持IE以外的瀏覽器。

    • Forever frame,支持IE瀏覽器。

    • Ajax long polling,支持所有瀏覽器。

    在服務(wù)器端及客戶端都支持的情況下,SignalR默認(rèn)會(huì)選擇最佳的傳輸方式。

    1.打開StockTicker.js,然后在客戶端與服務(wù)端建立連接之前加上下面這段代碼。

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);

    2.重新運(yùn)行程序,并打開瀏覽器的開發(fā)者工具,選擇控制臺(tái)標(biāo)簽,就可以看到SignalR輸出的日志(如果想看到全部的日志,請(qǐng)刷新頁(yè)面)。

    如果你是在Windows 8(IIS 8)上用IE10打開的話,將會(huì)看到WebSocket的連接方式。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    如果你是在Windows 7(IIS 7.5)上用IE10打開的話,將會(huì)看到使用iframe的連接方式。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    Windows 8(IIS 8)上用Firefox的話,將會(huì)看到WebSocket的連接方式。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    在Windows 7(IIS 7.5)上用Firefox打開的話,將會(huì)看到使用Server-sent events的連接方式。

    ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播

    “ASP.NET如何使用SignalR2實(shí)現(xiàn)服務(wù)器廣播”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI