1.新建ASP.NET Web应用程序, 选择Empty模板。
2.创建Stock.cs类
1 public class Stock 2 { 3 /// <summary> 4 /// 价格 5 /// </summary> 6 private decimal _price; 7 8 /// <summary> 9 /// 象征 10 /// </summary> 11 public string Symbol { get; set; } 12 13 public decimal Price 14 { 15 get 16 { 17 return _price; 18 } 19 set 20 { 21 if (_price == value) 22 { 23 return; 24 } 25 26 _price = value; 27 28 if (DayOpen == 0) 29 { 30 DayOpen = _price; 31 } 32 } 33 } 34 /// <summary> 35 /// 开放 36 /// </summary> 37 public decimal DayOpen { get; private set; } 38 39 /// <summary> 40 /// 变动 41 /// </summary> 42 public decimal Change 43 { 44 get 45 { 46 return Price - DayOpen; 47 } 48 } 49 50 /// <summary> 51 /// 变动百分比 52 /// </summary> 53 public double PercentChange 54 { 55 get 56 { 57 return (double)Math.Round(Change / Price, 4); 58 } 59 } 60 }
3.选择程序包管理控制台,添加SignalR Nuget包
输入命令 install-package Microsoft.AspNet.SignalR
4.选择添加SignalR Hub Class(v2),命名StockTickerHub
5.StockTickerHub.cs类
1 [HubName("stockTickerMini")] 2 public class StockTickerHub : Hub 3 { 4 private readonly StockTicker _stockTicker; 5 6 public StockTickerHub() : this(StockTicker.Instance) { } 7 8 public StockTickerHub(StockTicker stockTicker) 9 { 10 _stockTicker = stockTicker; 11 } 12 13 public IEnumerable<Stock> GetAllStocks() 14 { 15 return _stockTicker.GetAllStocks(); 16 } 17 }
6.添加StockTicker.cs类
1 public class StockTicker 2 { 3 // 单例线程 4 private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients)); 5 private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>(); 6 private readonly object _updateStockPricesLock = new object(); 7 private readonly double _rangePercent = .002; 8 private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250); 9 private readonly Random _updateOrNotRandom = new Random(); 10 private readonly Timer _timer; 11 //_updatingStockPrices标志被标记为volatile以确保对其进行线程安全访问。 12 private volatile bool _updatingStockPrices = false; 13 14 private StockTicker(IHubConnectionContext<dynamic> clients) 15 { 16 17 //构造函数使用一些示例股票数据初始化_stocks集合, 18 //GetAllStocks返回股票。 19 //这些股票集合又由StockTickerHub.GetAllStocks返回, 20 //这是客户可以调用的Hub类中的服务器方法。 21 Clients = clients; 22 _stocks.Clear(); 23 var stocks = new List<Stock> 24 { 25 new Stock { Symbol = "MSFT", Price = 30.31m }, 26 new Stock { Symbol = "APPL", Price = 578.18m }, 27 new Stock { Symbol = "GOOG", Price = 570.30m } 28 }; 29 stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock)); 30 31 //构造函数启动一个Timer对象, 32 //该对象定期调用随机更新股票价格的方法。 33 _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval); 34 35 } 36 37 public static StockTicker Instance 38 { 39 get 40 { 41 return _instance.Value; 42 } 43 } 44 45 private IHubConnectionContext<dynamic> Clients 46 { 47 get; 48 set; 49 } 50 51 public IEnumerable<Stock> GetAllStocks() 52 { 53 return _stocks.Values; 54 } 55 private void UpdateStockPrices(object state) 56 { 57 lock (_updateStockPricesLock) 58 { 59 if (!_updatingStockPrices) 60 { 61 _updatingStockPrices = true; 62 63 foreach (var stock in _stocks.Values) 64 { 65 if (TryUpdateStockPrice(stock)) 66 { 67 BroadcastStockPrice(stock); 68 } 69 } 70 71 _updatingStockPrices = false; 72 } 73 } 74 } 75 private bool TryUpdateStockPrice(Stock stock) 76 { 77 // Randomly choose whether to update this stock or not 78 var r = _updateOrNotRandom.NextDouble(); 79 if (r > .1) 80 { 81 return false; 82 } 83 84 // Update the stock price by a random factor of the range percent 85 var random = new Random((int)Math.Floor(stock.Price)); 86 var percentChange = random.NextDouble() * _rangePercent; 87 var pos = random.NextDouble() > .51; 88 var change = Math.Round(stock.Price * (decimal)percentChange, 2); 89 change = pos ? change : -change; 90 91 stock.Price += change; 92 return true; 93 } 94 95 private void BroadcastStockPrice(Stock stock) 96 { 97 Clients.All.updateStockPrice(stock); 98 } 99 }
7.添加Startup.cs类
1 public class Startup 2 { 3 public void Configuration(IAppBuilder app) 4 { 5 app.MapSignalR(); 6 } 7 }
8.设置前端代码
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <title>ASP.NET SignalR Stock Ticker</title> 5 <style> 6 body { 7 font-family: 'Segoe UI', Arial, Helvetica, sans-serif; 8 font-size: 16px; 9 } 10 11 #stockTable table { 12 border-collapse: collapse; 13 } 14 15 #stockTable table th, #stockTable table td { 16 padding: 2px 6px; 17 } 18 19 #stockTable table td { 20 text-align: right; 21 } 22 23 #stockTable .loading td { 24 text-align: left; 25 } 26 27 #stockTicker { 28 overflow: hidden; 29 width: 450px; 30 height: 24px; 31 border: 1px solid #999; 32 } 33 34 #stockTicker .inner { 35 width: 9999px; 36 } 37 38 #stockTicker ul { 39 display: inline-block; 40 list-style-type: none; 41 margin: 0; 42 padding: 0; 43 } 44 45 #stockTicker li { 46 display: inline-block; 47 margin-right: 8px; 48 } 49 50 /*<li data-symbol="{Symbol}"><span class="symbol">{Symbol}</span><span class="price">{Price}</span><span class="change">{PercentChange}</span></li>*/ 51 #stockTicker .symbol { 52 font-weight: bold; 53 } 54 55 #stockTicker .change { 56 font-style: italic; 57 } 58 </style> 59 </head> 60 <body> 61 <h1>ASP.NET SignalR Stock Ticker Sample</h1> 62 63 <h2>Live Stock Table</h2> 64 <div id="stockTable"> 65 <table border="1"> 66 <thead> 67 <tr><th>Symbol</th><th>Price</th><th>Open</th><th>Change</th><th>%</th></tr> 68 </thead> 69 <tbody> 70 <tr class="loading"><td colspan="5">loading...</td></tr> 71 </tbody> 72 </table> 73 </div> 74 75 <!--Script references. --> 76 <!--Reference the jQuery library. --> 77 <script src="/Scripts/jquery-1.10.2.min.js"></script> 78 <!--Reference the SignalR library. --> 79 <script src="/Scripts/jquery.signalR-2.1.2.js"></script> 80 <!--Reference the autogenerated SignalR hub script. --> 81 <script src="/signalr/hubs"></script> 82 <!--Reference the StockTicker script. --> 83 <script src="StockTicker.js"></script> 84 </body> 85 </html>
9.创建StockTicker.js
1 // A simple templating method for replacing placeholders enclosed in curly braces. 2 if (!String.prototype.supplant) { 3 String.prototype.supplant = function (o) { 4 return this.replace(/{([^{}]*)}/g, 5 function (a, b) { 6 var r = o[b]; 7 return typeof r === 'string' || typeof r === 'number' ? r : a; 8 } 9 ); 10 }; 11 } 12 13 $(function () { 14 15 var ticker = $.connection.stockTickerMini, // the generated client-side hub proxy 16 up = '▲', 17 down = '▼', 18 $stockTable = $('#stockTable'), 19 $stockTableBody = $stockTable.find('tbody'), 20 rowTemplate = '<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{DayOpen}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>'; 21 22 function formatStock(stock) { 23 return $.extend(stock, { 24 Price: stock.Price.toFixed(2), 25 PercentChange: (stock.PercentChange * 100).toFixed(2) + '%', 26 Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down 27 }); 28 } 29 30 function init() { 31 ticker.server.getAllStocks().done(function (stocks) { 32 $stockTableBody.empty(); 33 $.each(stocks, function () { 34 var stock = formatStock(this); 35 $stockTableBody.append(rowTemplate.supplant(stock)); 36 }); 37 }); 38 } 39 40 function scrollTicker() { 41 var w = $stockTickerUl.width(); 42 $stockTickerUl.css({ marginLeft: w }); 43 $stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker); 44 } 45 // Add a client-side hub method that the server will call 46 ticker.client.updateStockPrice = function (stock) { 47 var displayStock = formatStock(stock), 48 $row = $(rowTemplate.supplant(displayStock)); 49 50 $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']') 51 .replaceWith($row); 52 } 53 54 // Start the connection 55 $.connection.hub.start().done(init); 56 57 });
10.最终效果
具体可看微软官方:
https://docs.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr