谈一谈Signal R
Signal R 是一个用于 ASP.NET 开发的库,它简化了向应用程序添加实时 Web 功能的过程。实时 Web 功能指的是服务器代码能够在内容可用时立即推送到连接的客户端,而不是等待客户端请求新数据
实时通信:SignalR 允许服务器端代码通过远程过程调用 (RPC) 调用客户端浏览器中的 JavaScript 函数,从而实现实时通信
多种传输方式:SignalR 使用 WebSocket 作为主要传输方式,并在必要时回退到其他传输方式
连接管理:SignalR 自动处理连接管理,并允许广播消息到所有连接的客户端,或发送消息到特定客户端
扩展性:SignalR 应用程序可以扩展到数千个客户端,支持使用 Redis、SQL Server 或 Azure Service Bus 进行消息协调
在线的概念与传统的数据传输不同 他保持在线的时候,你在通讯。
Signal R 仅仅是服务的通讯。以这个服务服务为基础,可以影射到WPF的应用程序,MAUI的应用程序以及web assembly blazor. 各种应用程序通过服务连接起来.
整个过程是依据Microsoft.AspNetCore.SignalR.Client。
通讯过程放在下边这个hub里头,它是一个实体(class)。
很有意思的一点是,hub的定义是在服务端完成的。其他各个应用程序只要使用Microsoft.AspNetCore.SignalR.Client。就可以完成相互之间的通讯。当然,哈勃所承载的实体必须在各个客户端及服务端定义.
下面用两个实体student和teacher来表明HUB是怎么样承载这些实体并进行传输的
首先我们有两个class student teacher 一般来讲,这个被承载的实体的class应该是能够被json系列化的.
namespace BlazorSingal.Hubs
{
public class Student
{
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
// Other student-specific properties and methods can be added here.
}
public class Teacher
{
public int TeacherId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Subject { get; set; }
// Other teacher-specific properties and methods can be added here.
}
}
下面定义了一组用于发送这两个实体的方法。这里我们使用的All,假设是一个广播。事实上你有,你有其他的选择。Caller , Other Group
using Microsoft.AspNetCore.SignalR;
namespace BlazorSingal.Hubs
{
public class DataHubs : Hub
{
public async Task SendStudentAndTeacher(string user, Student student, Teacher teacher)
{
await Clients.All.SendAsync("ReceiveStudentAndTeacher", user, student, teacher);
}
public async Task NotifyStudent(string user, Student student)
{
await Clients.All.SendAsync("ReceiveStudentNotification", user, student);
}
public async Task AlertTeacher(string user, Teacher teacher)
{
await Clients.All.SendAsync("ReceiveTeacherAlert", user, teacher);
await Clients.Caller.SendAsync("ReceiveTeacherAlert", user, teacher);
}
}
}
SendStudentAndTeache, NotifyStudent AlertTeacher 是你将使用的发送方法
ReceiveStudentAndTeacher", ReceiveStudentNotification ReceiveTeacherAlert接受的关键字
User 是接收以后用来再分配的关键字,
下面是发送的举例 反正是C#在哪都行在什么app ,server.. maui
if (hubConnection is not null)
{
await hubConnection.SendAsync("NotifyStudent", "Send by student", student);
}
async Task Exe_SendTeacher()
{
M_state("Task SendTeacher ".Replace("_", " ") + " continue working!");
var cur_time = DateTime.Now;
await Task.Run(async () =>
{
var teacher = new Teacher()
{
LastName = t_WPFTacherpp.Split('_')[0]
};
await hubConn.SendAsync("ReceiveTeacherAlert", "Send by techer", teacher);
M_state("SendTeacher ".Replace("_", " ") +
DateTime.Now.Subtract(cur_time).TotalMinutes.ToString("F2")
+ " minute ");
});
}
下面是接收的状态 。也是,在哪都行,反正是c#
hubConnection.On<string, Student,Teacher>("ReceiveStudentAndTeacher", (user, Tstudent,Tteacher) =>
{
var encodedMsg = $"{user} : {student.LastName} {student.FirstName} {student.BirthDate} "+
$"th:{Tteacher.LastName} {Tteacher.FirstName} ";
messages.Add(encodedMsg);
InvokeAsync(StateHasChanged);
});
下面是一个prism中的例子我把它放在viewModel里头在
base.Check_base_timer_Tick(sender, e);
((DispatcherTimer)sender).Stop();
hubConn.On<string, Student, Teacher>("ReceiveStudentAndTeacher", (user, Tstudent, Tteacher) =>
{
t_Sender= user;
t_WPFStudentpp = new WPFStudent(Tstudent);
t_ReceiveList.Add(user+"-->"+
Tteacher.LastName + " _ " + Tteacher.FirstName +"_"+ Tteacher.TeacherId);
});
服务端的connection是通过依赖注册项来完成的
program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
#region for SignaR 依赖注册项
builder.Services.AddSignalRCore();
builder.Services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
["application/octet-stream"]);
});
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
{
builder.WithOrigins("https://example.com")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see Enforce HTTPS in ASP.NET Core | Microsoft Learn.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(BlazorSingal.Client._Imports).Assembly);
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.MapHub<DataHubs>("/DataHubs", options =>
{
// 在这里配置 HttpConnectionDispatcherOptions
// 例如,设置最大连接数、跨域策略等
options.Transports = HttpTransportType.WebSockets | HttpTransportType.LongPolling;
// 其他配置...
});
app.Run();
其他应用的hub connection的设置是一样的,反正通过c#完成。
DispatcherTimer check__SignalR_timer = new DispatcherTimer();
check__SignalR_timer.Tick += new EventHandler(CheckSingalR_timer_Tick);
check__SignalR_timer.Interval = new TimeSpan(0, 0, 5);
check__SignalR_timer.Start();
if (hubConn == null)
{
hubConn = new HubConnectionBuilder()
.WithUrl(O_setting.UrlST) // Replace with your actual hub URL
.WithAutomaticReconnect()
.Build();
}
建立连接以后,不管连上没连上 hubConn我经 有了 不是空的。State !=
在下面的循环中不断检查如果没连上告诉用户一声
async private void CheckSingalR_timer_Tick(object sender, EventArgs e)
{
((DispatcherTimer)sender).Stop();
t_ReceiveList.Add("start");
if(hubConn.State != HubConnectionState.Connected)
{
t_Color_StateSingalR = "#33AA0606";
t_StateSingalR = "Connecting";
// Create a cancellation token source.
using var cts = new CancellationTokenSource();
for (int i = 0; i < 3; i++)
{
try
{
await hubConn.StartAsync(cts.Token);
if (hubConn.State == HubConnectionState.Connected) goto Conn_ed;
}
catch when (cts.Token.IsCancellationRequested)
{
goto NotConn_ed;
}
catch
{
// Failed to connect; trying again after a delay.
await Task.Delay(5000, cts.Token);
}
}
NotConn_ed:;
((DispatcherTimer)sender).Start();
t_Color_StateSingalR = "#66AA0606";
t_StateSingalR = "Offline. Check your network";
return;
Conn_ed:;
t_Color_StateSingalR = "#FFFFFFFF";
t_StateSingalR = "OnLine";
}
else
{
if(t_StateSingalR != "OnLine")
{
t_Color_StateSingalR = "#FFFFFFFF";
t_StateSingalR = "OnLine";
}
}
((DispatcherTimer)sender).Start();
}
如果没连上,告诉用户一声,
可以参考下面的源程序,写的很乱,但意思表达清楚。