WPF+ASP.NET SignalR實(shí)現(xiàn)動(dòng)態(tài)折線圖的繪制
在實(shí)際業(yè)務(wù)中,當(dāng)后臺(tái)數(shù)據(jù)發(fā)生變化,客戶端能夠?qū)崟r(shí)的收到通知,而不是由用戶主動(dòng)的進(jìn)行頁(yè)面刷新才能查看,這將是一個(gè)非常人性化的設(shè)計(jì)。有沒(méi)有那么一種場(chǎng)景,后臺(tái)數(shù)據(jù)明明已經(jīng)發(fā)生變化了,前臺(tái)卻因?yàn)闆](méi)有及時(shí)刷新,而導(dǎo)致頁(yè)面顯示的數(shù)據(jù)與實(shí)際存在差異,從而造成錯(cuò)誤的判斷。那么如何才能在后臺(tái)數(shù)據(jù)變更時(shí)及時(shí)通知客戶端呢?本文以一個(gè)簡(jiǎn)單的動(dòng)態(tài)折線圖示例,簡(jiǎn)述如何通過(guò)ASP.NET SignalR實(shí)現(xiàn)后臺(tái)通知功能,僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。
什么是SignalR
ASP.NET SignalR 是一個(gè)面向 ASP.NET 開(kāi)發(fā)人員的庫(kù),可簡(jiǎn)化將實(shí)時(shí) web 功能添加到應(yīng)用程序的過(guò)程。 實(shí)時(shí) web 功能是讓服務(wù)器代碼將內(nèi)容推送到連接的客戶端立即可用,而不是讓服務(wù)器等待客戶端請(qǐng)求新數(shù)據(jù)的能力。
SignalR做了什么
傳統(tǒng)HTTP采用的是大家熟知的“拉模式”,即客戶端發(fā)出的每次請(qǐng)求,服務(wù)端都是被動(dòng)處理。此場(chǎng)景下客戶端是老大,很顯然只有一方主動(dòng),操作與處理起來(lái)就沒(méi)那么完美。
為了能讓服務(wù)端也能主動(dòng),html5的出現(xiàn)讓這種變得可能,大家知道html5中有兩種主動(dòng)模式。第一種叫做websockect,WebSockets是Html5提供的新的API,可以在Web網(wǎng)頁(yè)與服務(wù)器端間建立Socket連接,它是基于tcp模式的雙工通訊。還有一種叫做SSE,也就是客戶端來(lái)訂閱服務(wù)器的一種事件模型。
在html5出來(lái)之前,如果要做到服務(wù)器主動(dòng),我們只能采用變相的longpool和iframe流勉強(qiáng)實(shí)現(xiàn)。
SignalR對(duì)上面四種方案進(jìn)行了高度的封裝,也就是說(shuō)signalR會(huì)在這四種技術(shù)中根據(jù)瀏覽器和服務(wù)器設(shè)置采取最優(yōu)的一種模式。
封裝與集成
對(duì)于.NET開(kāi)發(fā)者的福音,.NET平臺(tái)為我們提供了一種簡(jiǎn)潔高效智能的實(shí)時(shí)信息交互技術(shù)->SignalR,它集成了上述數(shù)種技術(shù),并能根據(jù)配置自動(dòng)或手動(dòng)選擇其最佳應(yīng)用。
SignalR用途
SignalR 提供了一個(gè)簡(jiǎn)單的 API,用于創(chuàng)建服務(wù)器到客戶端遠(yuǎn)程過(guò)程調(diào)用 (RPC) ,該調(diào)用客戶端瀏覽器 (和其他客戶端平臺(tái)中的 JavaScript 函數(shù)) 服務(wù)器端 .NET 代碼。 SignalR 還包括用于連接管理的 API (,例如連接和斷開(kāi)連接事件) ,以及分組連接。
雖然聊天通常被用作示例,但你可以做更多的事情。每當(dāng)用戶刷新網(wǎng)頁(yè)以查看新數(shù)據(jù)時(shí),或者該網(wǎng)頁(yè)實(shí)施 Ajax 長(zhǎng)輪詢以檢索新數(shù)據(jù)時(shí),它都是使用 SignalR 的候選者。SignalR 還支持需要從服務(wù)器進(jìn)行高頻更新的全新類(lèi)型的應(yīng)用,例如實(shí)時(shí)游戲。
官方網(wǎng)址和源碼
官方網(wǎng)址:https://dotnet.microsoft.com/zh-cn/apps/aspnet/signalr
微軟API文檔:https://learn.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/introduction-to-signalr
GitHub網(wǎng)址:https://github.com/SignalR
示例截圖
本示例主要實(shí)現(xiàn)一個(gè)動(dòng)態(tài)折線圖功能,主要分為服務(wù)端和客戶端兩部分,示例如下所示:
服務(wù)端項(xiàng)目創(chuàng)建
1. 創(chuàng)建一個(gè)Web服務(wù)端程序(以ASP.NET WebApi為例),默認(rèn)情況下SignalR已經(jīng)作為項(xiàng)目框架的一部分而存在,所以不需要安裝,直接使用即可。通過(guò)項(xiàng)目--依賴(lài)性--框架--Microsoft.AspNetCore.App可以查看
2. 創(chuàng)建ChatHub,繼承Hub基類(lèi),作為后臺(tái)連接管理的中心
using Microsoft.AspNetCore.SignalR; namespace DemoSignalR.Server.Chat { public class ChatHub : Hub { #region 連接和斷開(kāi)連接 public override async Task OnConnectedAsync() { var connId = Context.ConnectionId; Console.WriteLine($"{connId} 已連接"); await base.OnConnectedAsync(); } public void StartNotify(string type) { if (type == "1") { } else if (type == "2") { }; } public override async Task OnDisconnectedAsync(Exception ex) { //如果斷開(kāi)連接 var connId = Context.ConnectionId; Console.WriteLine($"{connId} 已斷開(kāi)"); await base.OnDisconnectedAsync(ex); } #endregion } }
SignalR服務(wù)端業(yè)務(wù)集成
在實(shí)際業(yè)務(wù)中,存在各種需要后臺(tái)通知的功能,根據(jù)不同的業(yè)務(wù),可以采用不同的通知觸發(fā)方式:
1. 在調(diào)用接口時(shí)觸發(fā)后臺(tái)通知
using DemoSignalR.Server.Chat; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; namespace DemoSignalR.Server.Controllers { [ApiController] [Route("[controller]")] public class TestWebApiController : ControllerBase { private readonly ILogger<TestWebApiController> _logger; private IHubContext<ChatHub> _context; public TestWebApiController(ILogger<TestWebApiController> logger, IHubContext<ChatHub> context) { _logger = logger; _context = context; } [HttpGet] public void GetTestA(string Name) { string info = $"當(dāng)前接收到的信息為:{Name},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"; _context.Clients.All.SendAsync("", info); } } }
2. 定時(shí)器循環(huán)通知
using Microsoft.AspNetCore.SignalR; using System.Timers; namespace DemoSignalR.Server.Chat { public class TestChatInfo { private IHubContext<ChatHub> _context; private System.Timers.Timer _timer; private readonly static object locker = new object();//鎖對(duì)象 public static TestChatInfo instance;//當(dāng)前實(shí)例 private readonly ILogger _logger; private int _index = 0; private TestChatInfo(IHubContext<ChatHub> _context, ILogger _logger) { this._context = _context; this._logger = _logger; //定義定時(shí)器 _timer = new System.Timers.Timer(100); _timer.AutoReset = true; _timer.Enabled = true; _timer.Elapsed += Timer_Elapsed; _timer.Start(); } private void Timer_Elapsed(object? sender, ElapsedEventArgs e) { //業(yè)務(wù)邏輯判斷 var obj = new TestValue(); obj.Index = this._index; obj.Value = DateTime.Now.Millisecond % 100; _context.Clients.All.SendAsync("RefreshInfos", obj); this._index++; } /// <summary> /// 注冊(cè),即初始化單例實(shí)例 /// </summary> /// <param name="context"></param> /// <param name="reviewTaskService"></param> /// <param name="sysParamService"></param> public static void Register(IHubContext<ChatHub> context, ILogger logger) { lock (locker) { if (instance == null) { lock (locker) { instance = new TestChatInfo(context, logger); } } } } } public class TestValue { private int index; public int Index { get { return index; } set { index = value; } } private float value; public float Value { get { return value; } set { this.value = value; } } } }
SignalR服務(wù)端配置
SignalR服務(wù)端配置主要分成三步:
1. 添加SignalR服務(wù)
2. 映射SignalR路由
3. 注冊(cè)單例后臺(tái)通知服務(wù)(如果其他方式,可省略)
using DemoSignalR.Server.Chat; using Microsoft.AspNetCore.SignalR; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); //1.添加SignalR服務(wù) builder.Services.AddSignalR(); builder.Services.AddLogging(logging => logging.AddConsole()); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseRouting(); app.UseHttpsRedirection(); app.UseAuthorization(); //在Use中注冊(cè)單例實(shí)例 app.Use(async (context, next) => { var hubContext = context.RequestServices.GetRequiredService<IHubContext<ChatHub>>(); //var logger = context.RequestServices.GetRequiredService<ILogger>(); TestChatInfo.Register(hubContext, null);//調(diào)用靜態(tài)方法注冊(cè) if (next != null) { await next.Invoke(); } }); app.MapControllers(); //2.映射路由 app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chat"); }); app.Run();
客戶端項(xiàng)目創(chuàng)建
1. 創(chuàng)建WPF項(xiàng)目
2. 通過(guò)NuGet包管理器安裝SignalR客戶端
3. 創(chuàng)建SignalR狀態(tài)管理,主要管理SignalR的連接,關(guān)閉,重連等操作。
using Microsoft.AspNetCore.SignalR.Client; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoSignalR.Client { internal class SignalRClient { private readonly HubConnection hubConnection; public HubConnection HubConnection { get { return hubConnection; } } public SignalRClient() { var server = ConfigurationManager.AppSettings["Server"].ToString(); hubConnection = new HubConnectionBuilder().WithUrl($"{server}/chat").WithAutomaticReconnect().Build(); hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5); } public virtual void Listen() { } public async void Start() { try { await hubConnection.StartAsync(); } catch (Exception e) { } } } }
客戶端業(yè)務(wù)邏輯處理
1. 根據(jù)不同的業(yè)務(wù)邏輯分別監(jiān)聽(tīng)不同的通知事件。
2. 示例詳見(jiàn)源碼
using Microsoft.AspNetCore.SignalR.Client; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DemoSignalR.Client { internal class TestSignalRClient : SignalRClient { public Action<object> RefreshInfos; public Action<string> Reconnected; public TestSignalRClient() : base() { } public override void Listen() { HubConnection.On<object>("RefreshInfos", (obj) => { // if (obj != null) { Console.WriteLine("收到數(shù)據(jù)"); //發(fā)送消息 if (RefreshInfos != null) { RefreshInfos(obj); } } }); HubConnection.Reconnected += HubConnection_Reconnected; } private Task HubConnection_Reconnected(string arg) { return Task.Run(() => { if (Reconnected != null) { Reconnected(arg); } }); } public virtual void StartNotify(string condition) { HubConnection.SendAsync("StartNotify", condition); Console.WriteLine($"開(kāi)始通過(guò)知{condition}"); } } }
SignalR需要注意事項(xiàng)
你不會(huì)實(shí)例化 Hub 類(lèi)或從服務(wù)器上自己的代碼調(diào)用其方法;由 SignalR Hubs 管道為你完成的所有操作。 SignalR 每次需要處理中心操作(例如客戶端連接、斷開(kāi)連接或向服務(wù)器發(fā)出方法調(diào)用時(shí))時(shí),SignalR 都會(huì)創(chuàng)建 Hub 類(lèi)的新實(shí)例。
由于 Hub 類(lèi)的實(shí)例是暫時(shí)性的,因此無(wú)法使用它們來(lái)維護(hù)從一個(gè)方法調(diào)用到下一個(gè)方法的狀態(tài)。 每當(dāng)服務(wù)器從客戶端收到方法調(diào)用時(shí),中心類(lèi)的新實(shí)例都會(huì)處理消息。 若要通過(guò)多個(gè)連接和方法調(diào)用來(lái)維護(hù)狀態(tài),請(qǐng)使用一些其他方法(例如數(shù)據(jù)庫(kù))或 Hub 類(lèi)上的靜態(tài)變量,或者不派生自 Hub的其他類(lèi)。 如果在內(nèi)存中保留數(shù)據(jù),請(qǐng)使用 Hub 類(lèi)上的靜態(tài)變量等方法,則應(yīng)用域回收時(shí)數(shù)據(jù)將丟失。
如果要從在 Hub 類(lèi)外部運(yùn)行的代碼將消息發(fā)送到客戶端,則無(wú)法通過(guò)實(shí)例化 Hub 類(lèi)實(shí)例來(lái)執(zhí)行此操作,但可以通過(guò)獲取對(duì) Hub 類(lèi)的 SignalR 上下文對(duì)象的引用來(lái)執(zhí)行此操作。
注意:ChatHub每次調(diào)用都是一個(gè)新的實(shí)例,所以不可以有私有屬性或變量,不可以保存對(duì)像的值,所以如果需要記錄一些持久保存的值,則可以采用靜態(tài)變量,或者中心以外的對(duì)象。
關(guān)于源碼
本示例中相關(guān)源碼,已上傳至gitee(碼云),鏈接如下:https://gitee.com/ahsiang/demo-signal-r
以上就是WPF+ASP.NET SignalR實(shí)現(xiàn)動(dòng)態(tài)折線圖的繪制的詳細(xì)內(nèi)容,更多關(guān)于WPF繪制動(dòng)態(tài)折線圖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#訪問(wèn)C++動(dòng)態(tài)分配的數(shù)組指針(實(shí)例講解)
下面小編就為大家分享一篇C#訪問(wèn)C++動(dòng)態(tài)分配的數(shù)組指針(實(shí)例講解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12C# 實(shí)現(xiàn)SDL2進(jìn)行視頻播放窗口截圖和字幕添加
這篇文章主要介紹了C# 實(shí)現(xiàn)SDL2進(jìn)行視頻播放窗口截圖和字幕添加,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Unity實(shí)現(xiàn)顏色漸變滑動(dòng)條
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)顏色漸變滑動(dòng)條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07C#將字節(jié)數(shù)組轉(zhuǎn)換成數(shù)字的方法
這篇文章主要介紹了C#將字節(jié)數(shù)組轉(zhuǎn)換成數(shù)字的方法,涉及C#類(lèi)型轉(zhuǎn)換的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04C#通過(guò)html調(diào)用WinForm的方法
這篇文章主要介紹了C#通過(guò)html調(diào)用WinForm的方法,涉及html頁(yè)面中使用JavaScript訪問(wèn)C#的相關(guān)技巧,需要的朋友可以參考下2016-04-04利用微軟com組件mstscax.dll實(shí)現(xiàn)window7遠(yuǎn)程桌面功能
利用微軟提供的com組件mstscax.dll實(shí)現(xiàn)類(lèi)似window遠(yuǎn)程桌面功能,大家參考使用吧2013-12-12