微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言

如果对您有所帮助:欢迎赞赏

使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)

阅读导航

  1. 本文背景
  2. 代码实现
  3. 本文参考

1.本文背景

工作上有个业务,.Net Core WebAPI作为服务端,需要将运行过程中产生的日志分类,并实时推送到各种终端进行报警,终端有桌面(WPF)、移动(Xamarin.Forms)、网站(Angular.JS)等,使用SignalR进行警报日志推送。

下面是桌面端的测试效果:

2.代码实现

整个系统由服务端、桌面端、网站、移动端组成,结构如下:

2.1 服务端与客户端都使用的日志实体类

简单的日志定义,服务端会主动将最新日志通过AlarmLogItem实例推送到各个终端:

/// <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; }
} public enum AlarmLogType
{
Info,
Warn,
Error
}

2.2 服务端

使用 .Net Core 2.2 搭建的Web API项目

2.2.1 集线器类AlarmLogHub.cs

定义集线器Hub类AlarmLogHub,继承自Hub,用于SignalR通信,看下面的代码,没加任何方法,您没看错:

public class AlarmLogHub : Hub
{}

2.2.2 Startup.cs

需要在此类中注册SignalR管道及服务,在下面两个关键方法中用到,B/S后端的朋友非常熟悉了。

  1. ConfigureServices方法

添加SignalR管道(是这个说法吧?):

services.AddSignalR(options => { options.EnableDetailedErrors = true; });
  1. Configure方法注册SignalR服务地址

端口用的8022,客户端访问地址是:http://localhost:8022/alarmlog

app.UseSignalR(routes =>
{
routes.MapHub<AlarmLogHub>("/alarmlog");
});

2.2.3 SignalRTimedHostedService.cs

这是个关键类,用于服务端主动推送日志使用,Baidu、Google好久才找到,站长技术栈以C/S为主,B/S做的不多,没人指点,心酸,参考网址:How do I push data from hub to client every second using SignalR

该类继承自IHostedService,作为服务自启动(乱说的),通过SignalRTimedHostedService 的构造函数依赖注入得到IHubContext<AlarmLogHub>的实例,用于服务端向各客户端推送日志使用(在StartAsync方法中开启定时器,模拟服务端主动推送警报日志,见 DoWork 方法):

internal 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();
}
}

SignalRTimedHostedService 类作为Host服务(继承自 IHostedService),需要在Startup.cs的ConfigureServices方法中注册管道(是吧?各位有没有B/S比较好的书籍推荐,站长打算有空好好学学):

services.AddHostedService<SignalRTimedHostedService>();

服务端关键代码已经全部奉上,下面主要说说桌面端和移动端代码,其实两者代码类似。

2.3 网站

参考 index.html

2.4 桌面端(WPF)

使用 .Net Core 3.0创建的WFP工程,需要引入Nuget包:Microsoft.AspNetCore.SignalR.Client

界面用一个ListView展示收到的日志:

<Grid>
<ListBox x:Name="messagesList" RenderTransformOrigin="-0.304,0.109" BorderThickness="1" BorderBrush="Gainsboro"/>
</Grid>

后台写的简陋,直接在窗体构造函数中连接服务端SignalR地址:http://localhost:8022/alarmlog, 监听服务端警报日志推送:ReceiveAlarmLog。

using AppClient.Models;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using System.Windows; namespace SignalRChatClientCore
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
HubConnection connection;
public MainWindow()
{
InitializeComponent(); connection = new HubConnectionBuilder()
.WithUrl("http://localhost:8022/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);
}
}
}
}

2.4 移动端

移动端其实和桌面端类似,因为桌面端使用的 .Net Core 3.0,移动端使用的 .NET Standard 2.0,都需要引入Nuget包:Microsoft.AspNetCore.SignalR.Client。

界面使用ListView展示日志,这就不贴代码了,使用的MVVM方式,直接贴ViewModel代码吧,大家只看个大概,不要纠结具体代码,参照桌面.cs代码,是不是一样的?

using AppClient.Models;
using AppClient.Views;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Linq; namespace AppClient.ViewModels
{
/// <summary>
/// 报警日志VM
/// </summary>
public class AlarmItemsViewModel : BaseViewModel
{
private ViewState _state = ViewState.Disconnected; /// <summary>
/// 报警日志列表
/// </summary>
public ObservableCollection<AlarmLogItem> AlarmItems { get; set; }
public Command LoadItemsCommand { get; set; } //连接报警服务端
private HubConnection _connection; public AlarmItemsViewModel()
{
Title = "报警日志";
AlarmItems = new ObservableCollection<AlarmLogItem>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand()); //收到登录成功通知
MessagingCenter.Subscribe<LoginViewModel, LoginUser>(this, "LoginSuccess", async (sender, userInfo) =>
{
//DisplayAlert("登录成功", userInfo.UserName, "确定");
});
MessagingCenter.Subscribe<NewItemPage, AlarmLogItem>(this, "添加项", async (obj, item) =>
{
var newItem = item as AlarmLogItem;
AlarmItems.Add(newItem);
await DataStore.AddItemAsync(newItem);
}); ConnectAlarmServer();
} async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return; IsBusy = true; try
{
AlarmItems.Clear();
var items = await DataStore.GetItemsAsync(true);
foreach (var item in items)
{
AlarmItems.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
} private async Task ConnectAlarmServer()
{
if (_state == ViewState.Connected)
{
try
{
await _connection.StopAsync();
}
catch (Exception ex)
{
return;
}
_state = ViewState.Disconnected;
}
else
{
try
{
_connection = new HubConnectionBuilder()
.WithUrl(App.Setting.AlarmHost)
.Build();
_connection.On<AlarmLogItem>("ReceiveAlarmLog", async (newItem) =>
{
AlarmItems.Add(newItem);
await DataStore.AddItemAsync(newItem);
});
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
await _connection.StartAsync();
}
catch (Exception ex)
{
return;
}
_state = ViewState.Connected;
}
} private enum ViewState
{
Disconnected,
Connecting,
Connected,
Disconnecting
}
}
}

关键代码已经贴完了,希望对大家能有所帮助。

3.参考

  1. .NET 客户端 SignalR ASP.NET Core
  2. SignalR-samples
  3. How do I push data from hub to client every second using SignalR

除非注明,文章均由 Dotnet9 整理发布,欢迎转载。


转载请注明本文地址:https://dotnet9.com/6913.html


欢迎扫描下方二维码关注 Dotnet9 的微信公众号,本站会及时推送最新技术文章


使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)的更多相关文章

  1. 1.使用SignalR实现页面即时刷新(服务端主动推送)

    模块功能说明: 实现技术:sqlserver,MVC,WebAPI,ADO.NET,SignalR(服务器主动推送) 特殊车辆管理--->移动客户端采集数据存入数据库---->只要数据库数 ...

  2. 使用SignalR实现页面即时刷新(服务端主动推送)

    模块功能说明: 实现技术:sqlserver,MVC,WebAPI,ADO.NET,SignalR(服务器主动推送) 特殊车辆管理--->移动客户端采集数据存入数据库---->只要数据库数 ...

  3. 使用SignalR实现服务端消息推送

    概述 这篇文章参考的是Server Broadcast with SignalR 2这篇教程,很不错的一篇教程,如果有兴趣的话可以查看原文,今天记录下来作为一个学习笔记,这样今后翻阅会更方便一点. 这 ...

  4. Asp.net SignalR 实现服务端消息推送到Web端

              之前的文章介绍过Asp.net SignalR,  ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.  今天我 ...

  5. SignalR 实现web浏览器客户端与服务端的推送功能

    SignalR 是一个集成的客户端与服务器库,基于浏览器的客户端和基于 ASP.NET 的服务器组件可以借助它来进行双向多步对话. 换句话说,该对话可不受限制地进行单个无状态请求/响应数据交换:它将继 ...

  6. ASP.NET SignalR 系列(七)之服务端触发推送

    前面几章讲的都是从客户端触发信息推送的,但在实际项目中,很多信息可能是由系统服务端推送的,下面2图分别展示两种通道 客户端触发推送 服务端推送 下面我们就重点介绍下服务端如何调用集线器的对象进行推送 ...

  7. Spring mvc服务端消息推送(SSE技术)

    SSE技术是基于单工通信模式,只是单纯的客户端向服务端发送请求,服务端不会主动发送给客户端.服务端采取的策略是抓住这个请求不放,等数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求 ...

  8. SSE技术详解:使用 HTTP 做服务端数据推送应用的技术

    SSE ( Server-sent Events )是 WebSocket 的一种轻量代替方案,使用 HTTP 协议. 严格地说,HTTP 协议是没有办法做服务器推送的,但是当服务器向客户端声明接下来 ...

  9. SSE(Server-sent events)技术在web端消息推送和实时聊天中的使用

    最近在公司闲着没事研究了几天,终于搞定了SSE从理论到实际应用,中间还是有一些坑的. 1.SSE简介 SSE(Server-sent events)翻译过来为:服务器发送事件.是基于http协议,和W ...

随机推荐

  1. 关于SpringMVC未找到类[No qualifying bean of type [...]is defined]问题

    首先,不要慌,出现这个问题肯定是某一个配置注解没写上.. 无非就几个地方: 1.控制层是否添加@Controller or 需要pring管理的类是否添加@Component 2.DAO层是否添加@R ...

  2. AcWing 786.第k个数

    AcWing 786.第k个数 题目描述 给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少. 输入格式 第一行包含两个整数 n 和 k. 第二行包含 n 个整数( ...

  3. sql serverDB运维实用sql大全

    运维sql server的sql总结,包含阻塞语句.等待语句.某个时间段的sql性能查询等等常用sql语句 ##断开库的连接,记得修改库名 USE masterGOALTER DATABASE [DB ...

  4. python3 控制结构知识及范例

    (一)三种基本控制结构 1.顺序结构:按照程序语句编写的顺序依次逐条执行 2.选择结构:根据特定的条件选择某一个分支 3.循环结构:反复执行某个或者某些操作,关注点:在什么情况下,反复执行哪一部分的操 ...

  5. Codeforces_451_B

    http://codeforces.com/problemset/problem/451/B 取前后第一个不满足条件的位置,逆序,判断. #include<cstdio> #include ...

  6. 必知必会之Lambda表达式

    Java是一门强大的面向对象的语言,除了8种基本的数据类型,其他一切皆为对象.因此,在Java中定义函数或方法都离不开对象,也就意味着很难直接将方法或函数像参数一样传递,而Java8中的Lambda表 ...

  7. python学习(2)关于字符编码

    关于字符编码的学习内容笔记如下: 1.计算机只能用0和1来进行记录和存储.计算机是二进制. 2.ASCII(American Standard Code for Information Interch ...

  8. Flutter报错 Waiting for another flutter command to release the startup lock...

    Waiting for another flutter command to release the startup lock… 异常解决 平时我们在开发flutter过程中,在执行flutter p ...

  9. php curl 检测网页是否被百度收录

    <?php /* * 检测百度是否收录网页 curl模式 * @ param string $url传入的url* return int (1 收录 0 不收录) */ function che ...

  10. 常见Bash命令操作

    常见Bash命令操作 查看当前目录 pwd 查看目录下的文件 ls 进入某个目录 cd 返回上一级目录 cd .. 创建一个目录 mkdir abc 创建一个文件 touch a.html 保存文件退 ...