ASP.NET SignalR-Server Broadcast with SignalR

服务端通知表示发送到客户端的消息是由服务端发起的,这种情景与由一个客户端发出消息并通知其他所有客户端的情景不同。

创建一个股票报价机应用程序,由服务器端收集股票走势信息,并通知所有连接到服务端的客户端:
Stock.cs:

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

StockTickerHub.cs:

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

StockTicker.cs:

<span></span><pre style="" class="prettyprint prettyprinted"><span class="pln">
</span><span class="kwd">namespace</span><span class="pln"> </span><span class="typ">SignalR</span><span class="pun">.</span><span class="typ">StockTicker</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">StockTicker</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// Singleton instance</span><span class="pln">
        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">Lazy</span><span class="pun"><</span><span class="typ">StockTicker</span><span class="pun">></span><span class="pln"> _instance </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Lazy</span><span class="pun"><</span><span class="typ">StockTicker</span><span class="pun">>(()</span><span class="pln"> </span><span class="pun">=></span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StockTicker</span><span class="pun">(</span><span class="typ">GlobalHost</span><span class="pun">.</span><span class="typ">ConnectionManager</span><span class="pun">.</span><span class="typ">GetHubContext</span><span class="pun"><</span><span class="typ">StockTickerHub</span><span class="pun">>().</span><span class="typ">Clients</span><span class="pun">));</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="typ">ConcurrentDictionary</span><span class="pun"><</span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Stock</span><span class="pun">></span><span class="pln"> _stocks </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ConcurrentDictionary</span><span class="pun"><</span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Stock</span><span class="pun">>();</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="kwd">object</span><span class="pln"> _updateStockPricesLock </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">object</span><span class="pun">();</span><span class="pln">

        </span><span class="com">//stock can go up or down by a percentage of this factor on each change</span><span class="pln">
        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> _rangePercent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="lit">002</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="typ">TimeSpan</span><span class="pln"> _updateInterval </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TimeSpan</span><span class="pun">.</span><span class="typ">FromMilliseconds</span><span class="pun">(</span><span class="lit">250</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="typ">Random</span><span class="pln"> _updateOrNotRandom </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Random</span><span class="pun">();</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">readonly</span><span class="pln"> </span><span class="typ">Timer</span><span class="pln"> _timer</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> _updatingStockPrices </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">StockTicker</span><span class="pun">(</span><span class="typ">IHubConnectionContext</span><span class="str"><dynamic></span><span class="pln"> clients</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Clients</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> clients</span><span class="pun">;</span><span class="pln">

            _stocks</span><span class="pun">.</span><span class="typ">Clear</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> stocks </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">List</span><span class="pun"><</span><span class="typ">Stock</span><span class="pun">></span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stock</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"MSFT"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Price</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">30.31m</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stock</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"APPL"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Price</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">578.18m</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stock</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"GOOG"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Price</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">570.30m</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">};</span><span class="pln">
            stocks</span><span class="pun">.</span><span class="typ">ForEach</span><span class="pun">(</span><span class="pln">stock </span><span class="pun">=></span><span class="pln"> _stocks</span><span class="pun">.</span><span class="typ">TryAdd</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">.</span><span class="typ">Symbol</span><span class="pun">,</span><span class="pln"> stock</span><span class="pun">));</span><span class="pln">

            _timer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Timer</span><span class="pun">(</span><span class="typ">UpdateStockPrices</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> _updateInterval</span><span class="pun">,</span><span class="pln"> _updateInterval</span><span class="pun">);</span><span class="pln">

        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">StockTicker</span><span class="pln"> </span><span class="typ">Instance</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">get</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> _instance</span><span class="pun">.</span><span class="typ">Value</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">IHubConnectionContext</span><span class="str"><dynamic></span><span class="pln"> </span><span class="typ">Clients</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">get</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">set</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">IEnumerable</span><span class="pun"><</span><span class="typ">Stock</span><span class="pun">></span><span class="pln"> </span><span class="typ">GetAllStocks</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> _stocks</span><span class="pun">.</span><span class="typ">Values</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">UpdateStockPrices</span><span class="pun">(</span><span class="kwd">object</span><span class="pln"> state</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">lock</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_updateStockPricesLock</span><span class="pun">)</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">_updatingStockPrices</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                    _updatingStockPrices </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">

                    </span><span class="kwd">foreach</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> stock </span><span class="kwd">in</span><span class="pln"> _stocks</span><span class="pun">.</span><span class="typ">Values</span><span class="pun">)</span><span class="pln">
                    </span><span class="pun">{</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">TryUpdateStockPrice</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">))</span><span class="pln">
                        </span><span class="pun">{</span><span class="pln">
                            </span><span class="typ">BroadcastStockPrice</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">);</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                    </span><span class="pun">}</span><span class="pln">

                    _updatingStockPrices </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="typ">TryUpdateStockPrice</span><span class="pun">(</span><span class="typ">Stock</span><span class="pln"> stock</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// Randomly choose whether to update this stock or not</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> _updateOrNotRandom</span><span class="pun">.</span><span class="typ">NextDouble</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">r </span><span class="pun">></span><span class="pln"> </span><span class="pun">.</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">

            </span><span class="com">// Update the stock price by a random factor of the range percent</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> random </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Random</span><span class="pun">((</span><span class="kwd">int</span><span class="pun">)</span><span class="typ">Math</span><span class="pun">.</span><span class="typ">Floor</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">.</span><span class="typ">Price</span><span class="pun">));</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> percentChange </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="typ">NextDouble</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> _rangePercent</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> pos </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="typ">NextDouble</span><span class="pun">()</span><span class="pln"> </span><span class="pun">></span><span class="pln"> </span><span class="pun">.</span><span class="lit">51</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> change </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="typ">Round</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">.</span><span class="typ">Price</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">decimal</span><span class="pun">)</span><span class="pln">percentChange</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
            change </span><span class="pun">=</span><span class="pln"> pos </span><span class="pun">?</span><span class="pln"> change </span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="pln">change</span><span class="pun">;</span><span class="pln">

            stock</span><span class="pun">.</span><span class="typ">Price</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> change</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">BroadcastStockPrice</span><span class="pun">(</span><span class="typ">Stock</span><span class="pln"> stock</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Clients</span><span class="pun">.</span><span class="typ">All</span><span class="pun">.</span><span class="pln">updateStockPrice</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span>

 startup.cs: 

 public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }

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

});

result:

Loading

Initial stock table

Stock table receiving changes from server


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值