WeChat public address: Dotnet9 Website: Dotnet9 , questions or suggestions: Please leave a message on the website,
If it helps you: Welcome appreciation.
Reading navigation
- Background of this article
- code implementation
- Reference in this article
1. Background
There is a business at work. Net Core WebAPI, as the server, needs to classify the logs generated in the operation process and push them to various terminals in real time for alarm. The terminals include desktop (WPF), mobile (Xamarin.Forms), website (Angular.JS), etc. and use SignalR to push the alarm logs.
Here is the test effect of desktop:
2. Code implementation
The whole system is composed of server, desktop, website and mobile terminal, with the structure as follows:
2.1 log entity classes used by both server and client
For a simple log definition, the server will actively push the latest log to each terminal through the AlarmLogItem instance:
/// <summary> ///Alarm log /// </summary> public class AlarmLogItem { public string Id { get; set; } /// <summary> ///Log type /// </summary> public AlarmLogType Type { get; set; } /// <summary> ///Log name /// </summary> public string Text { get; set; } /// <summary> ///Log details /// </summary> public string Description { get; set; } /// <summary> ///Log update time /// </summary> public string UpdateTime { get; set; } } public enum AlarmLogType { Info, Warn, Error }
2.2 server
Web API project built with. Net Core 2.2
2.2.1 AlarmLogHub.cs of hub classDefine the Hub hub class AlarmLogHub, which inherits from Hub and is used for SignalR communication. Look at the following code without adding any methods. You are right:
public class AlarmLogHub : Hub {}
2.2.2 Startup.cs
You need to register the SignalR pipeline and services in this class, which are used in the following two key methods. The B/S backend friends are very familiar with it.
- ConfigureServices method
Add a SignalR pipeline :
services.AddSignalR(options => { options.EnableDetailedErrors = true; });
- Configure method register SignalR service address
Port 8022, the client access address is: http://localhost:8022/alarmlog
app.UseSignalR(routes => { routes.MapHub<AlarmLogHub>("/alarmlog"); });
2.2.3 SignalRTimedHostedService.cs
This is a key class, which is used by the server to actively push logs. Baidu and Google found it for a long time. The webmaster's technology stack is mainly C/S, and B/S does not do much. No one can point it out. Sorry. Refer to the website: How do I push data from hub to client every second using SignalR.
This class inherits from IHostedService. As a service self startup (nonsense), it obtains an instance of ihubcontext < alarmloghub > through the constructor dependency injection of SignalRTimedHostedService, which is used by the server to push logs to clients (open the timer in StartAsync method to simulate the active push of alert logs by the server, see the DoWork method):
internal class SignalRTimedHostedService : IHostedService, IDisposable { private readonly IHubContext<AlarmLogHub> _hub; private Timer _timer; //Analog send alarm log List<AlarmLogItem> lstLogs = new List<AlarmLogItem> { new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Attempt to connect 50 times, failed to reconnect!"}, new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK WebSocket Disconnection and reconnection",Description="Try to connect 5 times, successfully reconnected!"}, new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK Restfull Broken even",Description="Try to connect 30 times, successfully reconnected!"}, new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Disconnect for the first time!"}, new AlarmLogItem{ Type=AlarmLogType.Info,Text="OK WebSocket Successful connection",Description="First successful connection!"}, new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Attempt to connect the 7th time, failed to reconnect!"} }; 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(); } }
The SignalRTimedHostedService class, as the Host service (inherited from IHostedService), needs to register the pipeline in the ConfigureServices method of Startup.cs (right? Do you have a good B/S book recommendation? The webmaster plans to study hard when he has time):
services.AddHostedService<SignalRTimedHostedService>();
All the key codes of the server have been delivered. The following is mainly about the codes of the desktop and the mobile. In fact, the codes of the two are similar.
2.3 website
Reference resources index.html
2.4 desktop (WPF)
For the WFP project created with. Net Core 3.0, Nuget package needs to be introduced: Microsoft.AspNetCore.SignalR.Client
The interface uses a ListView to display the received logs:
<Grid> <ListBox x:Name="messagesList" RenderTransformOrigin="-0.304,0.109" BorderThickness="1" BorderBrush="Gainsboro"/> </Grid>
Simple background writing, directly connect the server SignalR address in the form constructor: http://localhost:8022/alarmlog , listen to the server alarm log push: 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 mobile terminal
In fact, the mobile terminal is similar to the desktop, because the. Net Core 3.0 used by the desktop and the. NET Standard 2.0 used by the mobile terminal need to introduce Nuget package: Microsoft.AspNetCore.SignalR.Client.
The interface uses ListView to display logs, so there's no code to paste. In MVVM mode, paste ViewModel code directly. Let's just look at it, don't get tangled up with specific code, refer to desktop. cs code, is it the same?
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> ///Alarm log VM /// </summary> public class AlarmItemsViewModel : BaseViewModel { private ViewState _state = ViewState.Disconnected; /// <summary> ///Alarm log list /// </summary> public ObservableCollection<AlarmLogItem> AlarmItems { get; set; } public Command LoadItemsCommand { get; set; } //Connect to alarm server private HubConnection _connection; public AlarmItemsViewModel() { Title = "Alarm log"; AlarmItems = new ObservableCollection<AlarmLogItem>(); LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand()); //Receive login success notification MessagingCenter.Subscribe<LoginViewModel, LoginUser>(this, "LoginSuccess", async (sender, userInfo) => { //DisplayAlert("login succeeded", userInfo.UserName, "OK"); }); MessagingCenter.Subscribe<NewItemPage, AlarmLogItem>(this, "Add item", 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 } } }
The key code has been pasted. I hope it can help you.
3. reference
- . NET client SignalR ASP.NET Core
- SignalR-samples
- How do I push data from hub to client every second using SignalR
Unless otherwise noted, articles are written by Dotnet9 Organize and release, welcome to reprint.
For reprint, please indicate the address: https://dotnet9.com/6913.html
Welcome to scan the WeChat public number below the two-dimensional code concern Dotnet9, this site will push the latest technical articles in time.