SignalR 是用于构建需要实时用户交互或实时数据更新的Web 应用程序的一个开放源代码.NET 库。不仅仅用在Web应用中,后面会讲到它的应用范围。它简化了简化了构建实时应用程序的过程,包括ASP.NET Server库和JavaScript Client库,以便管理Client与Server连接并将内容更新推送给Client。
SignalR可用于需要实时刷新获取后台数据的程序。常用的场景范围有:社交应用程序、 多用户游戏、 业务协作和新闻,天气或财务更新应用程序等等。
在传统的HTTP中,只能客户端主动向服务器端发起请求,服务器端无法主动向客户端发送消息。有的业务场景下,我们需要服务器端主动向客户端发送消息,比如Web聊天室、OA系统、站内消息等。
为了实现服务器端向客户端推送消息,在2008年诞生了WebSocket协议,并且该协议在2011年成为国际标准。目前所有的主流浏览器都已经支持WebSocket协议。WebSocket基于TCP(transmission control protocol,传输控制协议),支持二进制通信,因此通信效率非常高,它可以让服务器处理大量的并发WebSocket连接;WebSocket是双工通信,因此服务器可以高效地向客户端推送消息。
/// <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);
}
}
}
}
}
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();
客户端下载安装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
#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