SignalR消息推送
不久前我看到一篇用asp.netcore调用SignalR做日志推送的博客,觉得蛮不错,就按照作者的教程实际攒了一套webapi,感觉确实挺好的,又查了一些关于SignalR的资料,做了一些完善,在这里进行汇总。
1. Signal概要
SignalR的实现机制与/.NET WCF或Remoting是相似的,都是使用远程代理来实现。它要实现的场景类似于实时的Web服务,实时web功能是让服务器代码将内容推送到连接的客户端立即可用,而不是让服务器等待客户端请求新数据的能力。这点儿类似于WebSocket。
2. 服务端代码
- 使用asp.net core 新建一个WebApi的项目
- nuget安装Microsoft.AspNetCore.SignalR类库
- 在项目根目录新建Services目录,在其中添加两个类
- AlarmLogHub.cs
public class AlarmLogHub:Hub
{
}
- SignalRTimedHostedService.cs
public class SignalRTimedHostedService : IHostedService, IDisposable
{
private readonly IHubContext<AlarmLogHub> _hub;
private Timer _timer;
//模拟发送报警日志
List<AlarmLogItem> lstLogs = new List<AlarmLogItem> {
new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket断连",Description="尝试连接50次,未成功重连!"},
new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK WebSocket断开重连",Description="尝试连接5次,成功重连!"},
new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK Restfull断连",Description="尝试连接30次,成功重连!"},
new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket断连",Description="第一次断开链接!"},
new AlarmLogItem{ Type=AlarmLogType.Info,Text="OK WebSocket连接成功",Description="首次成功连接!"},
new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket断连",Description="尝试连接第7次,未成功重连!"}
};
Random rd = new Random(DateTime.Now.Millisecond);
public SignalRTimedHostedService(IHubContext<AlarmLogHub> hub)
{
_hub = hub;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(1));
return Task.CompletedTask;
}
private void DoWork(object state)
{
if (DateTime.Now.Second % rd.Next(1, 3) == 0)
{
AlarmLogItem log = lstLogs[rd.Next(lstLogs.Count)];
log.UpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
_hub.Clients.All.SendAsync("ReceiveAlarmLog", log);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
- 在启动设置文件Startup.cs注入服务
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHostedService<SignalRTimedHostedService>();
services.AddSignalR(options => { options.EnableDetailedErrors = true; });
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<AlarmLogHub>("/alarmlog");
});
}
- Model实体代码
/// <summary>
/// 报警日志
/// </summary>
public class AlarmLogItem
{
public string Id { get; set; }
/// <summary>
/// 日志类型
/// </summary>
public AlarmLogType Type { get; set; }
/// <summary>
/// 日志名称
/// </summary>
public string Text { get; set; }
/// <summary>
/// 日志详细信息
/// </summary>
public string Description { get; set; }
/// <summary>
/// 日志更新时间
/// </summary>
public string UpdateTime { get; set; }
}
/// <summary>
/// 报警日志类型
/// </summary>
public enum AlarmLogType
{
Info,
Warn,
Error
}
3. 客户端代码
- 客户端采用wpf创建,用于接收服务端推送的消息
- 用nuget安装 Microsoft.AspNetCore.SignalR.Client
- 前端页面MainWindow.xaml增加列表用于显示接收到的消息
<Grid>
<ListBox x:Name="messagesList" RenderTransformOrigin="-0.304,0.109" BorderThickness="1" BorderBrush="Gainsboro"/>
</Grid>
- 后端MainWindow.xaml.cs的代码
public partial class MainWindow : Window
{
HubConnection connection;
public MainWindow()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("https://localhost:44334/alarmlog")
.Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
connection.On<AlarmLogItem>("ReceiveAlarmLog", (message) =>
{
this.Dispatcher.Invoke(() =>
{
messagesList.Items.Add(message.Description);
});
});
try
{
connection.StartAsync();
messagesList.Items.Add("Connection started");
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
}