SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

  • SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?已关闭评论
  • 85 次浏览
  • A+
所属分类:.NET技术
摘要

官网文档:https://learn.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-6.0&tabs=visual-studio
SignalR开源代码:https://github.com/signalr


官网文档:https://learn.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-6.0&tabs=visual-studio
SignalR开源代码:https://github.com/signalr

很多小伙伴问:在前后端分离项目中,后端是.NET Core前端是Vue如何使用SignalR?在前后端不分离项目中,.NET Framework MVC项目中又如何使用SignalR技术呢?那就来看看下面这篇文章吧!本文主要介绍SignalR在实际项目中的应用,以及.NET Framework和.NET Core中如何去使用SignalR。

一、SignalR介绍

1.1-SignalR介绍

ASP.NET Core SignalR是一个开放源代码库,可用于简化向应用添加实时Web功能,实时Web功能使服务器端代码能够将内容推送到客户端。

1.2-SignalR的应用

  • 需要从服务器进行高频更新的应用:包括游戏、社交网络、投票、拍卖、地图和GPS引用。
  • 仪表盘和监视应用:包括公司仪表板、即时销售更新或旅行报警
  • 协作应用:包括白板应用和团队会议软件。
  • 通知应用:社交网络、电子邮件、聊天、游戏、旅行报警和其他应用都需要使用的通知。

二、.NET Framework使用SignalR

参考文献:
Owin介绍:https://www.cnblogs.com/Pinapple/p/6721361.html
Owin相关案例:https://blog.csdn.net/bifan546/article/details/77098896/

2.1-服务端(.NET Framework MVC)

(1)选择要使用Signalr的项目,点击【管理NuGet程序包】。
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(2)搜索【SignalR】包,找到这个后然后点击下载。
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?
会自动安装四个,注意他们之间有依赖关系:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(3).NET Framework平台需要添加Owin相关的包。
OWIN 是一种定义 Web 服务器和应用程序组件之间的交互的规范 。这一规范的目的是发展一个广阔且充满活力的、基于 Microsoft .NET Framework 的 Web 服务器和应用程序组件生态系统。

Microsoft.Owin.Hosting
Microsoft.Owin.Cors
Microsoft.Owin.Host.HttpListener

(4)在Web项目(要使用的Signal)中创建一个【Startup.cs】文件。

using JiCai.Admin.Hubs; using Microsoft.Owin; using Owin;  [assembly: OwinStartup(typeof(JiCai.Admin.Startup))] namespace JiCai.Admin {     public class Startup     {         /// <summary>         /// 应用程序配置         /// </summary>         /// <param name="app"></param>         public void Configuration(IAppBuilder app)         {             //启用SignalR             app.MapSignalR();             //绑定多个Hub             app.MapSignalR<DemoHub>("/demoHub");         }     } } 

例如:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(5)可以创建一个【Hubs】文件夹,专门放置Hub相关文件。[西瓜程序猿]这边是创建一个Hub文件,名为【DemoHub.cs】。

using Fenqibao.DTO; using JiCai.Admin.Hubs.ConectionOperate; using JiCai.Admin.Hubs.Models; using log4net; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Json; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks;  namespace JiCai.Admin.Hubs {     /// <summary>     /// 报表管理-总表Hub     /// </summary>     public class SummaryTableHub :  PersistentConnection     {         public readonly BaseService _base = new BaseService();         private readonly ILog logger = LogManager.GetLogger(typeof(SummaryTableHub));         private ConnectionManagement summaryTableCon = new ConnectionManagement();         public CookieUserData LoginUserData         {             get             {                 IOperator oper = ContainerManager.Resolve<IOperator>();                 LoginUser loginUser = oper as LoginUser;                 if (loginUser != null && loginUser.UserData != null)                 {                     return loginUser.UserData;                 }                 return null;             }         }          /// <summary>         /// 连接成功后调用         /// </summary>         /// <param name="request"></param>         /// <param name="connectionId"></param>         /// <returns></returns>         protected override Task OnConnected(IRequest request, string connectionId)         {             //获得SignalR的连接id             var connid = connectionId;             //获得用户id             var userid = LoginUserData.Id.ToString();             Console.Write($"【{connid}】:已建立连接!");              //判断一下用户是不是已经链接了             var checkUserConn = summaryTableCon.IsConn(connid, userid);             if (!checkUserConn)             {                 //添加一个新的连接                 summaryTableCon.AddConnInfo(new SignalRConn()                 {                     UserId = userid,                     ConnectionId = connid                 });             }             //更新连接             else             {                 summaryTableCon.UpdateConnInfo(userid, connid);             }              return Connection.Send(connectionId, $"【用户:{connectionId}】真正连接成功!");             //return base.OnConnected(request, connectionId);         }          /// <summary>         /// 接收到请求的时候调用         /// </summary>         /// <param name="request"></param>         /// <param name="connectionId"></param>         /// <param name="data"></param>         /// <returns></returns>         protected override async Task OnReceived(IRequest request, string connectionId, string data)         {             //获得用户id             var userid = LoginUserData.Id.ToString();             await Task.Factory.StartNew(async () =>             {                 while (true)                 { 					var list = GetSummaryTableList(userid);                     string json_jieshou_mes = "";                     if (list != null && list.Count > 0)                     { 						json_jieshou_mes = JsonConvert.SerializeObject(list);                     }                       await Connection.Send(connectionId, json_jieshou_mes);                      //每5秒同步一次                     await Task.Delay(5000);                 }             }, TaskCreationOptions.LongRunning);              //return base.OnReceived(request, connectionId, data);         }          /// <summary>         /// 连接中断的时候调用         /// </summary>         /// <param name="request"></param>         /// <param name="connectionId"></param>         /// <param name="stopCalled"></param>         /// <returns></returns>         protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)         {             Console.Write($"【{connectionId}】:已断开连接!");             //获得SignalR的连接id             var connid = connectionId;             //关闭连接             summaryTableCon.DelConnInfo(connid);             return base.OnDisconnected(request, connectionId, stopCalled);         }          /// <summary>         /// 连接超时重新连接的时候调用         /// </summary>         /// <param name="request"></param>         /// <param name="connectionId"></param>         /// <returns></returns>         protected override Task OnReconnected(IRequest request, string connectionId)         {             return base.OnReconnected(request, connectionId);         }   		/// <summary> 		/// 查询数据 		/// </summary> 		/// <param name="userId"></param> 		/// <returns></returns> 		private List<SummaryTableDataModel> GetSummaryTableList(string userId)         {             var result = _base.Query<SummaryTableDataModel>($@" 			    select * from demo-data 					;             ").ToList(); 			return result;         }     } } 

(6)在Hubs/ConectionOperate文件夹中,[西瓜程序猿]这边创建【ConnectionManagement】文件,用来管理所有连接。

using System.Collections.Generic; using System.Linq;  namespace JiCai.Admin.Hubs.ConectionOperate {     /// <summary>     /// 连接管理     /// </summary>     public class ConnectionManagement     {         /// <summary>         /// 用户连接集合         /// </summary>         public static  List<SignalRConn> SignalRConns { get; set; } = new List<SignalRConn>();          /// <summary>         /// 添加连接         /// </summary>         /// <param name="conn"></param>         public  void AddConnInfo(SignalRConn conn)         {             SignalRConns.Add(conn);         }          /// <summary>         /// 删除连接         /// </summary>         /// <param name="connid"></param>         public  void DelConnInfo(string connid)         {             var signalRConns = SignalRConns.FirstOrDefault(u => u.ConnectionId == connid);             if (signalRConns != null)             {                 SignalRConns.Remove(signalRConns);             }         }          /// <summary>         /// 更新链接(老的链接不起作用了)         /// 场景:客户端重连了,userid没变,但是connid变了         /// </summary>         /// <param name="userId">用户id</param>         /// <param name="newConnsId">新的链接id</param>         public  void UpdateConnInfo(string userId, string newConnsId)         {             var signalRConns = SignalRConns.FirstOrDefault(u => u.UserId.ToLower() == userId.ToLower());             if (signalRConns != null)             {                 signalRConns.ConnectionId = newConnsId;             }         }          /// <summary>         /// 判断用户是否已经链接         /// </summary>         /// <param name="connid">连接id</param>         /// <param name="userid">用户id</param>         /// <returns></returns>         public bool IsConn(string connid,string userid)         {             var userConn = SignalRConns.FirstOrDefault(u => u.ConnectionId.ToLower() == connid.ToLower() && u.UserId.ToLower() == userid.ToLower());             return userConn == null ? false : true;         }     } } 

(7)在Hubs/ConectionOperate文件夹中,创建【SignalRConn.cs】文件用来作为SignalR和系统用户的连接实体。

namespace JiCai.Admin.Hubs.ConectionOperate {     /// <summary>     /// 连接     /// </summary>     public class SignalRConn     {         /// <summary>         /// 系统用户id         /// </summary>         public string UserId { get; set; }          /// <summary>         /// SignleR链接Id(每次链接SignalR都会分配一个id)         /// </summary>         public string ConnectionId { get; set; }     } } 

2.2-客户端(JS)

(1)下载相关jq/signalr相关包,分别是【jquery.signalR-2.4.3.js】和【jquery-1.6.4.min.js】。可以访问下载(如果失效了,请联系我[西瓜程序猿])。

下载地址(编码:yRLCRp81):https://yongteng.lanzoub.com/iXDlu1631ugd
密码:44x5

文件截图:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(2)创建一个js文件【data_list_table_hub.js】,用来连接signalR。

// 连接服务 var connection = $.connection("/summary_table_hub");  // 建立链接 connection.start(function () {     //连接成功     console.log("西瓜程序猿-【" + new Date().toLocaleString() +"】连接成功!");     //发送消息     connection.send("给我数据吧"); });  // 连接断开 connection.disconnected(function () {     console.log("西瓜程序猿-【" + new Date().toLocaleString() +"】连接断开!"); });  // 接收服务器发来的消息 connection.received(function (data) {     console.log("西瓜程序猿-【" + new Date().toLocaleString() +"】接收服务器发来的消息:");     console.log(data);     //显示数据     if (data != "" && checkJson(data)) {         var obj = JSON.parse(data);          var html_box = "";         for (var i = 0; i < obj.length; i++) {             html_box += `<tr>             <td>`+obj[i].project_name+`</td>             <td>`+ obj[i].zuori_sum+`</td>             <td>`+ obj[i].jinri_sum+`</td>             <td>`+ obj[i].qunian_sum+`</td>             <td>`+ obj[i].jinnian_sum+`</td>             <td>`+ obj[i].sum+`</td>             <td>`+ obj[i].yikaipiao_sum+`</td>             <td>`+ obj[i].weikaipiao_sum +`</td>             <td>`+ obj[i].yishoupiao_sum +`</td>             <td>`+ obj[i].weishoupiao_sum +`</td>             <td>`+ obj[i].kehu_yinghuikuan_sum+`</td>             <td>`+ obj[i].kehu_yihuikuan_sum+`</td>             <td>`+ obj[i].kehu_weihuikuan_sum +`</td>             <td>`+ obj[i].fuwu_yingfukuan_sum+`</td>             <td>`+ obj[i].fuwu_yifukuan_sum+`</td>             <td>`+ obj[i].fuwu_weifukuan_sum+`</td>             </tr>`         }         $("#last_async_date").text(new Date().toLocaleString());         $("#table_body").html(html_box);     } });  //判断是不是json字符串 function checkJson(str) {     if (typeof str == 'string') {         try {             var obj = JSON.parse(str);             // 等于这个条件说明就是JSON字符串 会返回true             if (typeof obj == 'object' && obj) {                 return true;             } else {                 //不是就返回false                 return false;             }         } catch (e) {             return false;         }     }     return false; }  

(3)创建要展示的文件,我这边是用MVC模式,所有前台文件是cshtml。

@{     ViewBag.Title = "西瓜程序猿-明细表"; }  @section styles{     <style>         .table_tr_center {             text-align: center !important;         }          .table_tr_bg_0 {             background: rgb(253,226,226);         }                    .table_tr_bg_1 {             background: rgb(218,226,246);         }          .table_tr_bg_2 {             background: rgb(228,237,219);         }          .table_tr_bg_3 {             background: rgb(249,229,215);         }     </style> }  @section scripts{     <script src="~/Scripts/jquery-1.6.4.min.js"></script>     <script src="~/Scripts/jquery.signalR-2.4.3.js"></script>     <script src="~/Scripts/controller/hubs/[email protected]"></script> }  <div class="row">     <div class="col-xs-12">     </div>     <div class="col-xs-12">         <div>             报表最新同步时间:<span id="last_async_date" style="color: #FF5722 !important;"></span>         </div>         <div class="layui-form">             <table class="layui-table">                 <colgroup>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                     <col>                 </colgroup>                 <thead>                     <tr>                         <th colspan="2" class="table_tr_center table_tr_bg_0">项目信息</th>                         <th colspan="5" class="table_tr_center table_tr_bg_1">销售情况</th>                         <th colspan="6" class="table_tr_center table_tr_bg_3">款项情况</th>                     </tr>                     <tr>                         <th class="table_tr_center">项目名称</th>                         <th class="table_tr_center">服务商名称</th>                         <th class="table_tr_center">昨天订单总额</th>                         <th class="table_tr_center">今天订单总额</th>                         <th class="table_tr_center">去年订单总额</th>                         <th class="table_tr_center">今年订单总额</th>                         <th class="table_tr_center">订单总额</th>                          <th class="table_tr_center">客户应回款总额</th>                         <th class="table_tr_center">客户已回款总额</th>                         <th class="table_tr_center">客户未回款总额</th>                         <th class="table_tr_center">服务商应付款总额</th>                         <th class="table_tr_center">服务商已付款总额</th>                         <th class="table_tr_center">服务商未付款总额</th>                     </tr>                 </thead>                 <tbody id="table_body">                 </tbody>             </table>         </div>     </div> </div>  

(4)效果展示:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

三、.NET Core WebAPI使用SignalR

场景:项目中需要服务端主动向客户端发送通知消息,后端是使用.NETCore实现的,前端是使用Vue3全家桶项目,前后端分离模式。本次主要讲使用SignalR如何来实现的。

3.1-服务端(.NET Core WebAPI)

1、右击项目,点击【管理NuGet程序包】安装SignalR。
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

2、搜索【SignalR】,点击【安装】。
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

3、如果是.NET6以前的版本,在【Startup.cs】中配置:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?
如果是.NET6以后得版本,在【Program.cs】配置:

var builder = WebApplication.CreateBuilder(args); //添加SignalR服务 builder.Services.AddSignalR();   app.UseEndpoints(endpoints => {     endpoints.MapControllerRoute(         name: "default",         pattern: "{controller=Home}/{action=Index}/{id?}"     );      //添加SignalR端点     endpoints.MapHub<ServerMonitorHub>("/serverMonitorHub"); }); 

SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

4、创建SignalR中心

中心是一个类,用于处理客户端<——>服务器通信的高级管道。在SignalR_Demo项目文件夹中,创建Hubs文件夹。在Hubs文件夹中,使用已下代码创建ChatHub类。

    public class ChatHub:Hub     {         /// <summary>         /// 发送消息         /// </summary>         /// <param name="user">用户名</param>         /// <param name="message">发送信息</param>         /// <returns></returns>         public async Task SendMessage(string user,string message)         {             await Clients.All.SendAsync("ReceiveMessage", user, message);         }     } 

3.2-客户端(Vue3+Vite+TS)

(1)安装SugbalR

npm install @latelier/vue-signalr 

版本截图:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(2)然后新建一个文件,用来封装业务逻辑相关代码。[西瓜程序猿]是在【src/utils】目录下,创建了一个名为【signalr.ts】的文件,也可以是js文件,根据自己项目的需要去新建。
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?
代码:

import * as signalR  from '@microsoft/signalr';  //如果需要身份验证 //.withUrl('/messageHub', {accessTokenFactory: () => sessionStorage.getItem('token')}) let connection;  // 建立连接 async function start(url) {   try {     connection = new signalR.HubConnectionBuilder()       .withUrl(url)//跨域需要使用绝对地址       .configureLogging(signalR.LogLevel.Information)       .withAutomaticReconnect() // 设置自动重连机制       .build();   } catch(err) {     console.log(err);     setTimeout(start, 10000);//错误重连   } }  // 开始signalr连接 const connect = async (url) => {   await start(url);   console.log(`【西瓜程序猿-${new Date().toLocaleString()}:SignalR已连接成功!`); };  // 调用服务端方法 async function send(methodName, param){   try {     await connection.invoke(methodName, param);   } catch (err) {     console.error(err);   } }  //断开连接 const disconnect = async ()=>{   await connection.stop();   console.log(`【西瓜程序猿-${new Date().toLocaleString()}:SignalR已断开连接!`); };  export {   connection,   connect,   send,   disconnect }; 

(3)然后再页面进行使用。[西瓜程序猿]这里以Echarts图表展示为例子,首先先安装Echarts包。

npm install echarts --save

版本截图:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

(4)然后再页面写入如下代码,具体页面样式根据需要进行修改调整哦。

<!--  * @Author: Kimi Liu  * @Date: 2023-05-24 21:11:24  * @LastEditors: Kimi Liu  * @LastEditTime: 2023-07-07 16:18:55  * @FilePath: KimiToolBoxsrcviewsmonitorserverindex.vue  * @Description: 服务监控  * Copyright (c) 2023 by kimiliu.cn, All Rights Reserved. --> <template>   <!-- 面包屑导航 -->   <breadcrumb class="breadcrumb_container" />   <div class="app_main_padding">      <el-row>        <el-col :span="24" class="card-box">         <el-card>           <template #header>             <span><i class="el-icon-monitor"></i> 服务器信息</span>           </template>           <div class="el-table el-table--enable-row-hover el-table--medium">             <table cellspacing="0" style="width: 100%;">               <tbody>                 <tr>                   <td class="el-table__cell is-leaf"><div class="cell">主机名称</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.HostName}}</div></td>                   <td class="el-table__cell is-leaf"><div class="cell">系统名称</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.OSDescription}}</div></td>                 </tr>                 <tr>                   <td class="el-table__cell is-leaf"><div class="cell">IP地址</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.IpAddress}}</div></td>                   <td class="el-table__cell is-leaf"><div class="cell">操作系统</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.OperatingSystem}} {{serverInfo.OsArchitecture}}</div></td>                 </tr>               </tbody>             </table>           </div>         </el-card>       </el-col>         <el-col :span="24" class="card-box">         <el-card>           <template #header>             <span><i class="el-icon-monitor"></i> 应用信息</span>           </template>           <div class="el-table el-table--enable-row-hover el-table--medium">             <table cellspacing="0" style="width: 100%;">               <tbody>                 <tr>                   <td class="el-table__cell is-leaf"><div class="cell">.NET Core版本</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.FrameworkDescription}}</div></td>                   <td class="el-table__cell is-leaf"><div class="cell">内存占用</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.MemoryFootprint}}</div></td>                 </tr>                 <tr>                   <td class="el-table__cell is-leaf"><div class="cell">环境变量</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.EnvironmentName}}</div></td>                   <td class="el-table__cell is-leaf"><div class="cell">项目路径</div></td>                   <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.ContentRootPath}}</div></td>                 </tr>               </tbody>             </table>           </div>         </el-card>       </el-col>        <el-col :span="24" class="card-box">         <el-card>           <template #header>             <span><i class="el-icon-monitor"></i> 磁盘状态</span>           </template>           <div class="el-table el-table--enable-row-hover el-table--medium">             <table cellspacing="0" style="width: 100%;" class="el-table__header">               <thead>                 <tr>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">磁盘名称</div></th>                   <th class="el-table_2_column_11 is-leaf headWeight el-table__cell"><div>盘符路径</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">文件系统</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">盘符类型</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">总大小</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">已用大小</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">可用大小</div></th>                   <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">已使用率</div></th>                 </tr>               </thead>               <tbody v-if="diskInfos" class="table_tbody">                 <tr v-for="(sysFile, index) in diskInfos" :key="index">                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.DiskName }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.RootPath }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.DriveType }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.FileSystem }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.TotalSize }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.UseSize }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.ResidueSize }}</div></td>                   <td class="el-table_2_column_11 el-table__cell"><div class="cell"><el-progress :percentage="sysFile.UseRate" :text-inside="true" :stroke-width="14" :color="customColor"/></div></td>                 </tr>               </tbody>             </table>           </div>         </el-card>       </el-col>           <el-col :span="24" class="card-box">         <el-card class="box-card">           <template #header>             <span><i class="el-icon-monitor"></i> 状态</span>           </template>           <div>             <el-row>               <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">                 <div class="title">CPU使用率</div>                 <div class="content">                   <el-progress type="dashboard" :percentage="cpuInfo.CPULoadVal" :color="customColor" />                 </div>                            <div class="footer">{{ cpuInfo.ProcessorCount }} 核心</div>               </el-col>                               <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">                 <div class="title">内存使用率</div>                 <div class="content">                   <el-progress type="dashboard" :percentage="memoryInfo.UsedPercentage" :color="customColor" />                 </div>                 <div class="footer">{{ memoryInfo.UsedPhysicalMemory }} / {{ memoryInfo.TotalPhysicalMemory }}</div>               </el-col>                 <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">                 <div class="title">网络监控上传</div>                 <div class="content">                   <el-progress type="dashboard" :percentage="networkInfo.SentSize" :color="customColor" >                     <template #default>                       {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}                     </template>                   </el-progress>                 </div>                 <div class="footer"> {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}/S</div>               </el-col>                 <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">                 <div class="title">网络监控下载</div>                 <div class="content">                   <el-progress type="dashboard" :percentage="networkInfo.ReceivedSize" :color="customColor" >                     <template #default>                       {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}                     </template>                   </el-progress>                 </div>                 <div class="footer"> {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}/S</div>               </el-col>             </el-row>           </div>         </el-card>       </el-col>           <el-col :span="24" class="card-box">         <el-row :gutter="25">             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">               <el-card class="box-card">                 <template #header>                   <span><i class="el-icon-monitor"></i> CPU使用率监控</span>                 </template>                 <div>                   <div id="chart1" class="chart"></div>                 </div>               </el-card>             </el-col>             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">               <el-card class="box-card">                 <template #header>                   <span><i class="el-icon-monitor"></i> 内存使用率监控</span>                 </template>                 <div>                   <div id="chart2" class="chart"></div>                 </div>               </el-card>             </el-col>           </el-row>       </el-col>      </el-row>   </div> </template>  <script lang="ts" setup> import { ref, onMounted, onBeforeUnmount, reactive } from 'vue'; import { EChartsOption, init } from 'echarts'; import { connection, connect, disconnect } from '@/utils/signalr'; import { getApiUrl } from '@/apis/common/utils';  // CPU监控图标 const cpuOption = reactive<EChartsOption>(   {     tooltip: {       trigger: 'axis'     },     xAxis: {       type: 'category',       boundaryGap: false,       data: []     },     yAxis: {       type: 'value',       min: 0,       max: 100,       interval: 20,       axisLabel: {         formatter: '{value}%'       }     },     series: [       {         data: [],         type: 'line'       }     ]   } );   const memoryOption = reactive<EChartsOption>(   {     tooltip: {       trigger: 'axis'     },     xAxis: {       type: 'category',       boundaryGap: false,       data: []     },     yAxis: {       type: 'value',       min: 0,       max: 100,       interval: 20,       axisLabel: {         formatter: '{value}%'       }     },     series: [       {         data: [],         type: 'line'       }     ]   } );   // 获得服务器信息 const serverInfo = ref(); const cpuInfo = ref(); const memoryInfo = ref(); const diskInfos = ref([]); const networkInfo = ref();  const getServerInfos = (res) => {   serverInfo.value = res.Server;   cpuInfo.value = res.CPU;   memoryInfo.value = res.Memory;   diskInfos.value = res.Disk;   networkInfo.value = res.Network;    if(cpuOption.xAxis.data.length >= 8) {     cpuOption.xAxis.data.shift();     cpuOption.series[0].data.shift();      memoryOption.xAxis.data.shift();     memoryOption.series[0].data.shift();   }    cpuOption.xAxis.data.push(res.Time);   cpuOption.series[0].data.push(res.CPU.CPULoadVal);    memoryOption.xAxis.data.push(res.Time);   memoryOption.series[0].data.push(res.Memory.UsedPercentage);    // 获取dom,断言HTMLElement类型,否则会报错   const chartEle: HTMLElement = document.getElementById('chart1') as HTMLElement;   const chart = init(chartEle);   chart.setOption(cpuOption);    // 获取dom,断言HTMLElement类型,否则会报错   const chartEle2: HTMLElement = document.getElementById('chart2') as HTMLElement;   const chart2 = init(chartEle2);   chart2.setOption(memoryOption);    // 重置图表大小   window.addEventListener('resize', () => {     chart.resize();     chart2.resize();   }); };  onMounted(async () => {   // 建立连接   connect('http://192.168.11:8080/serverMonitorHub');    // 注册方法(接收服务器推送过来的数据)   connection.on('ReceiveMessage', (res) => {     console.log(`【${new Date().toLocaleString()}】:从服务器同步成功!`);     if(res) {       const result = JSON.parse(res);       getServerInfos(result);     }   });    // 开始连接   await connection.start(); });   // 卸载 onBeforeUnmount(() => {   // 断开连接   disconnect(); });  // 进度条颜色 const customColor = ref('#ff7831'); </script>  <style lang="scss" scoped> .card-box { 	padding-right: 15px; 	margin-bottom: 15px; }  :deep(.el-card.is-always-shadow) {   box-shadow: 0 5px 8px 0 rgba(0,0,0,0.03);   border: none; }  .chart {   width: 100%;   height: 300px; } .box_item {   display: flex;   justify-content: center;   flex-direction: column;   align-items: center; }  .table_tbody {   tr td{     padding: 15px 0;     text-align: center;   } } </style> 

(5)效果展示:
SignalR实战:在.NET Framework和.NET Core中如何使用SignalR?

原文链接:https://www.cnblogs.com/kimiliucn/p/17648543.html