## 一、什麼是SignalR: **SignalR** 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括**ASP.NET Server**庫和**JavaScript ...
一、什麼是SignalR:
SignalR 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括ASP.NET Server庫和JavaScript Client庫,以便管理Client與Server連接並將內容更新推送給Client。
SignalR可用於需要實時刷新獲取後臺數據的程式。常用的場景範圍有:社交應用程式、 多用戶游戲、 業務協作和新聞,天氣或財務更新應用程式等等。
二、關於WebSocket知識拓展:
在傳統的HTTP中,只能客戶端主動向伺服器端發起請求,伺服器端無法主動向客戶端發送消息。有的業務場景下,我們需要伺服器端主動向客戶端發送消息,比如Web聊天室、OA系統、站內消息等。
為了實現伺服器端向客戶端推送消息,在2008年誕生了WebSocket協議,並且該協議在2011年成為國際標準。目前所有的主流瀏覽器都已經支持WebSocket協議。WebSocket基於TCP(transmission control protocol,傳輸控制協議),支持二進位通信,因此通信效率非常高,它可以讓伺服器處理大量的併發WebSocket連接;WebSocket是雙工通信,因此伺服器可以高效地向客戶端推送消息。
三、建立SignalR服務端:
項目架構:
集線器服務定義:
/// <summary>
/// 定義集線器
/// </summary>
public class MyHub : Hub
{
/// <summary>
/// 用戶字典
/// </summary>
private static Dictionary<string, string> dictUsers = new Dictionary<string, string>();
/// <summary>
/// 建立連接回調
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
Console.WriteLine($"ID:{Context.ConnectionId} 已連接");
return base.OnConnectedAsync();
}
/// <summary>
/// 斷開連接回調
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception? exception)
{
Console.WriteLine($"ID:{Context.ConnectionId} 已斷開");
return base.OnDisconnectedAsync(exception);
}
/// <summary>
/// 登錄功能,將用戶ID和ConntectionId關聯起來
/// </summary>
/// <param name="userId"></param>
public void Login(string userId)
{
if (!dictUsers.ContainsKey(userId))
{
dictUsers[userId] = Context.ConnectionId;
}
Console.WriteLine($"{userId}登錄成功,ConnectionId={Context.ConnectionId}");
//向所有用戶發送當前線上的用戶列表
Clients.All.SendAsync("Users", dictUsers.Keys.ToList());
}
/// <summary>
/// 退出功能,當客戶端退出時調用
/// </summary>
/// <param name="userId"></param>
public void Logout(string userId)
{
if (dictUsers.ContainsKey(userId))
{
dictUsers.Remove(userId);
}
Console.WriteLine($"{userId}退出成功,ConnectionId={Context.ConnectionId}");
}
}
實時推送任務定義:
/// <summary>
/// 定義定時任務
/// </summary>
public class MyWorker
{
/// <summary>
/// 單例
/// </summary>
public static MyWorker Instance;
/// <summary>
/// 鎖
/// </summary>
private static readonly object locker = new object();
/// <summary>
/// 集線器請求上下文
/// </summary>
private IHubContext<MyHub> context;
/// <summary>
/// 定時器
/// </summary>
public static System.Timers.Timer timer;
/// <summary>
/// 構造註入
/// </summary>
/// <param name="context"></param>
public MyWorker(IHubContext<MyHub> context)
{
this.context = context;
timer = new System.Timers.Timer(1000);//單位毫秒
timer.Enabled = true;
timer.AutoReset = true;//自動重新
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
/// <summary>
/// 時鐘到達事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
//模擬數據,一般情況下,從資料庫獲取,然後通知到客戶端
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);
}
/// <summary>
/// 單例註冊服務
/// </summary>
/// <param name="context"></param>
public static void Register(IHubContext<MyHub> context)
{
if (Instance == null)
{
lock (locker)
{
if (Instance == null)
{
Instance = new MyWorker(context);
}
}
}
}
}
全局註冊SignaIR服務:
using Microsoft.AspNetCore.SignalR;
using SignalRApi.Hubs;
using SignalRApi.Jobs;
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服務
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中註冊單例實例
app.Use(async (context, next) =>
{
var hubContext = context.RequestServices
.GetRequiredService<IHubContext<MyHub>>();
MyWorker.Register(hubContext);//調用靜態方法註冊
if (next != null)
{
await next.Invoke();
}
});
app.MapControllers();
//2.映射路由
app.UseEndpoints(endpoints => {
endpoints.MapHub<MyHub>("/myhub");//啟用SignalR中間件,並且設置當客戶端通過SignalR請求“/myhub”這個路徑的時候,由ChatHub進行處理。
});
app.Run();
四、建立SignalR客戶端:
客戶端界面設計:
客戶端依賴:
客戶端下載安裝Nuget包:Microsoft.AspNetCore.SignalR.Client
dotnet add package Microsoft.AspNetCore.SignalR.Client --version 6.0.8
客戶端代碼—點擊事件定義:
#region 點擊事件
/// <summary>
/// 建立連接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btConnect_Click(object sender, EventArgs e)
{
//1.初始化
InitInfo();
//2.監聽
Listen();
//3.連接
Link();
//4.登錄
Login();
}
/// <summary>
///斷開連接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btDistinct_Click(object sender, EventArgs e)
{
hubConnection.InvokeAsync("Logout", "12345");
_isDistinct = true;
}
#endregion
客戶端代碼—SignaIR連接與監聽定義:
#region 連接和監聽
/// <summary>
/// 是否斷開連接
/// </summary>
private bool _isDistinct = false;
/// <summary>
/// 集線器連接對象
/// </summary>
private HubConnection hubConnection;
/// <summary>
/// 初始化Connection對象
/// </summary>
private void InitInfo()
{
hubConnection = new HubConnectionBuilder().WithUrl("http://localhost:5275/myhub").WithAutomaticReconnect().Build();//必須和在伺服器端MapHub註冊單例實例設置的路徑一致;
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
}
/// <summary>
/// 監聽
/// </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);
}
}
/// <summary>
/// /監聽事件
/// </summary>
/// <param name="data"></param>
private void ReceiveInfos(Dictionary<string, object> data)
{
if (data == null || data.Count < 1 || _isDistinct)
{
return;
}
this.tbOnline.Text = data["online"]?.ToString();
this.tbMale.Text = data["male"]?.ToString();
this.tbFemale.Text = data["female"]?.ToString();
}
/// <summary>
/// 登錄
/// </summary>
private void Login()
{
hubConnection.InvokeAsync("Login", "12345");
_isDistinct =false;
}
#endregion
五、運行演示:
啟動服務:
啟動客戶端:
接收實時數據推送:
服務端控制台列印輸出:
源碼鏈接地址:
Gitee完整項目實例地址:
本文來自博客園,作者:碼農阿亮,轉載請註明原文鏈接:https://www.cnblogs.com/wml-it/p/17656056.html
技術的發展日新月異,隨著時間推移,無法保證本博客所有內容的正確性。如有誤導,請大家見諒,歡迎評論區指正!
開源庫地址,歡迎點亮:
GitHub:https://github.com/ITMingliang
Gitee: https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang
建群聲明: 本著技術在於分享,方便大家交流學習的初心,特此建立【編程內功修煉交流群】,為大家答疑解惑。熱烈歡迎各位愛交流學習的程式員進群,也希望進群的大佬能不吝分享自己遇到的技術問題和學習心得!進群方式:掃碼關註公眾號,後臺回覆【進群】。