首页 > 代码库 > 【SignalR学习系列】4. SignalR广播程序

【SignalR学习系列】4. SignalR广播程序

创建项目

创建一个空的 Web 项目,并在 Nuget 里面添加 SignalR,jQuery UI 包,添加以后项目里包含了 jQuery,jQuery.UI ,和 SignalR 的脚本。

技术分享

 

服务端代码

创建 Stock 类

using System;namespace SignalRDemo4{    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);            }        }    }}

创建 tockTicker 和 StockTickerHub 类

技术分享

添加类 StockTicker

using Microsoft.AspNet.SignalR;using Microsoft.AspNet.SignalR.Hubs;using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.Linq;using System.Threading;using System.Web;namespace SignalRDemo4{    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);        }    }}

为了线程安全,把 Stock 的数据存储在了 ConcurrentDictionary 里面。

 

添加 SignalR Hub Class

using System;using System.Collections.Generic;using System.Linq;using System.Web;using Microsoft.AspNet.SignalR;using Microsoft.AspNet.SignalR.Hubs;namespace SignalRDemo4{    [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();        }    }}

添加 Owin 类,并在里面配置 SignalR

技术分享

using System;using System.Threading.Tasks;using Microsoft.Owin;using Owin;[assembly: OwinStartup(typeof(SignalRDemo4.Startup))]namespace SignalRDemo4{    public class Startup    {        public void Configuration(IAppBuilder app)        {            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888            app.MapSignalR();        }    }}

 

客户端代码

添加名为 StockTicker 的html页面,设置为启动页面,并替换为下面代码。

<!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-3.1.1.min.js"></script>    <!--Reference the SignalR library. -->    <script src="Scripts/jquery.signalR-2.2.2.js"></script>    <!--Reference the autogenerated SignalR hub script. -->    <script src="/signalr/hubs"></script>    <!--Reference the StockTicker script. -->    <script src="Scripts/StockTicker.js"></script></body></html>

添加名为 StockTicker 的js文件,并添加下面代码

// A simple templating method for replacing placeholders enclosed in curly braces.if (!String.prototype.supplant) {    String.prototype.supplant = function (o) {        return this.replace(/{([^{}]*)}/g,            function (a, b) {                var r = o[b];                return typeof r === ‘string‘ || typeof r === ‘number‘ ? r : a;            }        );    };}$(function () {    var ticker = $.connection.stockTickerMini, // the generated client-side hub proxy        up = ‘▲‘,        down = ‘▼‘,        $stockTable = $(‘#stockTable‘),        $stockTableBody = $stockTable.find(‘tbody‘),        rowTemplate = ‘<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{DayOpen}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>‘;    function formatStock(stock) {        return $.extend(stock, {            Price: stock.Price.toFixed(2),            PercentChange: (stock.PercentChange * 100).toFixed(2) + ‘%‘,            Direction: stock.Change === 0 ? ‘‘ : stock.Change >= 0 ? up : down        });    }    function init() {        ticker.server.getAllStocks().done(function (stocks) {            $stockTableBody.empty();            $.each(stocks, function () {                var stock = formatStock(this);                $stockTableBody.append(rowTemplate.supplant(stock));            });        });    }    // Add a client-side hub method that the server will call    ticker.client.updateStockPrice = function (stock) {        var displayStock = formatStock(stock),            $row = $(rowTemplate.supplant(displayStock));        $stockTableBody.find(‘tr[data-symbol=‘ + stock.Symbol + ‘]‘)            .replaceWith($row);    }    // Start the connection    $.connection.hub.start().done(init);});

然后按F5进行调试,打开多个浏览器,发现里面的数据会同步更新。

技术分享

 

源代码链接:

链接: https://pan.baidu.com/s/1pKRIR2r 密码: em9k

 

参考链接:

https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr

【SignalR学习系列】4. SignalR广播程序