網(wǎng)站首頁 編程語言 正文
在實(shí)際業(yè)務(wù)中,當(dāng)后臺(tái)數(shù)據(jù)發(fā)生變化,客戶端能夠?qū)崟r(shí)的收到通知,而不是由用戶主動(dòng)的進(jìn)行頁面刷新才能查看,這將是一個(gè)非常人性化的設(shè)計(jì)。比如數(shù)字化大屏,并沒有人工的干預(yù),而是自動(dòng)的刷新數(shù)據(jù),那如何才能實(shí)現(xiàn)數(shù)據(jù)的實(shí)時(shí)刷新呢?本文以一個(gè)簡單示例,簡述如何通過WPF+ASP.NET SignalR實(shí)現(xiàn)消息后臺(tái)通知以及數(shù)據(jù)的實(shí)時(shí)刷新,僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。
?通過上一篇文章的學(xué)習(xí),了解了如何通過SignalR實(shí)現(xiàn)在線聊天功能,在示例中,我們發(fā)現(xiàn)每一次的客戶端連接都是一個(gè)新的實(shí)例對(duì)象,所以沒有辦法在中心對(duì)象中存儲(chǔ)狀態(tài)信息,所以為了存儲(chǔ)用戶列表,我們采用了靜態(tài)變量的方式。并且在線聊天功能是用戶發(fā)送一條消息(Chat),然后觸發(fā)中心對(duì)象(ChatHub),轉(zhuǎn)發(fā)給另一個(gè)用戶(SendAsync)。那么如果實(shí)現(xiàn)數(shù)字化大屏,需要服務(wù)端持續(xù)的往客戶端發(fā)送消息,而不是客戶端主動(dòng)觸發(fā),應(yīng)該怎么做呢?這就是本文需要分享的內(nèi)容。
涉及知識(shí)點(diǎn)
在本示例中,涉及知識(shí)點(diǎn)如下所示:
- 開發(fā)工具:Visual Studio 2022 目標(biāo)框架:.NET6.0
- ASP.NET SignalR,一個(gè)ASP .NET 下的類庫,可以在ASP .NET 的Web項(xiàng)目中實(shí)現(xiàn)實(shí)時(shí)通信,目前新版已支持.NET6.0及以上版本。在本示例中,作為消息通知的服務(wù)端。
- WPF,是微軟推出的基于Windows 的用戶界面框架,主要用于開發(fā)客戶端程序。
前提條件
實(shí)現(xiàn)服務(wù)端持續(xù)往客戶端發(fā)送消息,除了業(yè)務(wù)上的需求外,還需要滿足兩個(gè)條件:
- 在服務(wù)端有一個(gè)常駐內(nèi)存對(duì)象,監(jiān)聽數(shù)據(jù)變化。
- 常駐內(nèi)存對(duì)象,可以訪問中心對(duì)象(ChatHub),能夠獲取中心對(duì)象的所有連接客戶端,并發(fā)送消息。
滿足以上兩個(gè)條件,才可以實(shí)現(xiàn)想要的功能。
服務(wù)端
經(jīng)過以上分析后,服務(wù)端分為兩方面,核心對(duì)象(ChatHub),處理業(yè)務(wù)對(duì)象(Worker)。下面我們逐一說明:
ChatHub 中心是用于向連接到 SignalR 服務(wù)器的客戶端發(fā)送消息的核心抽象,負(fù)責(zé)客戶端的連接和斷開。如下所示:
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Chat
{
public class ChatHub:Hub
{
public override Task OnConnectedAsync()
{
Console.WriteLine($"ID:{Context.ConnectionId} 已連接");
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception? exception)
{
Console.WriteLine($"ID:{Context.ConnectionId} 已斷開");
return base.OnDisconnectedAsync(exception);
}
}
}
Worker實(shí)例為一個(gè)單例對(duì)象,常駐內(nèi)容,實(shí)時(shí)監(jiān)聽數(shù)據(jù)變化,并通過ChatHub上下文(IHubContext<ChatHub>)獲取連接信息,然后發(fā)送消息,如下所示:
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Chat
{
public class Worker
{
public static Worker Instance;
private static readonly object locker=new object();
private IHubContext<ChatHub> context;
private System.Timers.Timer timer;
public Worker(IHubContext<ChatHub> context) {
this.context = context;
timer= new System.Timers.Timer(500);//單位毫秒
timer.Enabled=true;
timer.AutoReset=true;//自動(dòng)重新
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
//模擬數(shù)據(jù),一般情況下,從數(shù)據(jù)庫獲取,然后通知到客戶端
Dictionary<string, object> data = new Dictionary<string, object>();
var online = new Random().Next(0, 100);
var male = Math.Floor(new Random().NextSingle() * online);
var female = online - male;
data["online"]=online;
data["male"] =male;
data["female"] = female;
context.Clients.All.SendAsync("Data",data);
}
public static void Register(IHubContext<ChatHub> context)
{
if (Instance == null)
{
lock (locker)
{
if (Instance == null)
{
Instance = new Worker(context);
}
}
}
}
}
}
注意:此處發(fā)送數(shù)據(jù)的是Data方法,客戶端必須監(jiān)聽Data方法,才能接收數(shù)據(jù)。
如何創(chuàng)建單例對(duì)象呢,中心對(duì)象上下文不能自己創(chuàng)建,必須要和ChatHub通過注入方式的上下文是同一個(gè),不然無法獲取客戶端連接信息。在項(xiàng)目啟動(dòng)時(shí),通過中間件的方式創(chuàng)建,如下所示:
using Microsoft.AspNetCore.SignalR;
using SignalRChat.Chat;
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();
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>>();
Worker.Register(hubContext);//調(diào)用靜態(tài)方法注冊(cè)
if (next != null)
{
await next.Invoke();
}
});
app.MapControllers();
//2.映射路由
app.UseEndpoints(endpoints => {
endpoints.MapHub<ChatHub>("/chat");
});
app.Run();
客戶端
客戶端主要是連接服務(wù)器,然后監(jiān)聽服務(wù)端發(fā)送數(shù)據(jù)的方法即可,如下所示:
namespace SignalRClient
{
public class ShowDataViewModel : ObservableObject
{
#region 屬性及構(gòu)造函數(shù)
private int online;
public int Online
{
get { return online; }
set { SetProperty(ref online, value); }
}
private int male;
public int Male
{
get { return male; }
set { SetProperty(ref male, value); }
}
private int female;
public int Female
{
get { return female; }
set { SetProperty(ref female, value); }
}
private HubConnection hubConnection;
public ShowDataViewModel()
{
}
#endregion
#region 命令
private ICommand loadedCommand;
public ICommand LoadedCommand
{
get
{
if (loadedCommand == null)
{
loadedCommand = new RelayCommand<object>(Loaded);
}
return loadedCommand;
}
}
private void Loaded(object obj)
{
//1.初始化
InitInfo();
//2.監(jiān)聽
Listen();
//3.連接
Link();
}
#endregion
/// <summary>
/// 初始化Connection對(duì)象
/// </summary>
private void InitInfo()
{
hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build();
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
}
/// <summary>
/// 監(jiān)聽
/// </summary>
private void Listen()
{
hubConnection.On<Dictionary<string,object>>("Data", ReceiveInfos);
}
/// <summary>
/// 連接
/// </summary>
private async void Link()
{
try
{
await hubConnection.StartAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void ReceiveInfos(Dictionary<string, object> data)
{
if (data == null || data.Count < 1)
{
return;
}
int.TryParse(data["online"]?.ToString(),out int online);
int.TryParse(data["male"]?.ToString(),out int male);
int.TryParse(data["female"]?.ToString(),out int female);
this.Online=online;
this.Male = male;
this.Female=female;
}
}
}
注意:監(jiān)聽Data方法,和服務(wù)端發(fā)送時(shí)保持一致。
運(yùn)行示例
在示例中,需要同時(shí)啟動(dòng)服務(wù)端和客戶端,所以以多項(xiàng)目方式啟動(dòng),如下所示:
運(yùn)行成功后,服務(wù)端以ASP.NET Web API的方式呈現(xiàn),如下所示:
客戶端運(yùn)行如下:
注意:客戶端可以有多個(gè),也可以是一個(gè),后臺(tái)通知消息,會(huì)通知到每一個(gè)連接的客戶端。
原文鏈接:https://www.cnblogs.com/hsiang/p/16687365.html
相關(guān)推薦
- 2022-06-06 uniApp實(shí)現(xiàn)滾動(dòng)視圖點(diǎn)擊錨點(diǎn)跳轉(zhuǎn)、點(diǎn)擊左側(cè)分欄時(shí)右側(cè)對(duì)應(yīng)內(nèi)容置頂、左右分欄聯(lián)動(dòng)、getSyste
- 2023-05-06 Python中dilb和face_recognition第三方包安裝失敗的解決_python
- 2022-07-13 CMD使用技巧和常用固定語句
- 2022-05-31 openCV顯著性檢測(cè)的使用_python
- 2022-08-28 go?zero微服務(wù)高在請(qǐng)求量下如何優(yōu)化_Golang
- 2022-05-10 bean基于xml文件
- 2021-12-10 linux系統(tǒng)文件共享samba配置教程_Linux
- 2022-08-08 基于Redis緩存數(shù)據(jù)常見的三種問題及解決_Redis
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支