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

2023年 8月 22日 24.2k 0

官网文档:learn.microsoft.com/zh-cn/aspne…

SignalR开源代码: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介绍:www.cnblogs.com/Pinapple/p/…

Owin相关案例:blog.csdn.net/bifan546/ar…

2.1-服务端(.NET Framework MVC)

(1)选择要使用Signalr的项目,点击【管理NuGet程序包】。image.png

(2)搜索【SignalR】包,找到这个后然后点击下载。image.png会自动安装四个,注意他们之间有依赖关系:image.png

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

Microsoft.Owin.HostingMicrosoft.Owin.CorsMicrosoft.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
    {
        /// 
        /// 应用程序配置
        /// 
        /// 
        public void Configuration(IAppBuilder app)
        {
            //启用SignalR
            app.MapSignalR();
            //绑定多个Hub
            app.MapSignalR("/demoHub");
        }
    }
}

例如:image.png

(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
{
    /// 
    /// 报表管理-总表Hub
    /// 
    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();
                LoginUser loginUser = oper as LoginUser;
                if (loginUser != null && loginUser.UserData != null)
                {
                    return loginUser.UserData;
                }
                return null;
            }
        }

        /// 
        /// 连接成功后调用
        /// 
        /// 
        /// 
        /// 
        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);
        }

        /// 
        /// 接收到请求的时候调用
        /// 
        /// 
        /// 
        /// 
        /// 
        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);
        }

        /// 
        /// 连接中断的时候调用
        /// 
        /// 
        /// 
        /// 
        /// 
        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);
        }

        /// 
        /// 连接超时重新连接的时候调用
        /// 
        /// 
        /// 
        /// 
        protected override Task OnReconnected(IRequest request, string connectionId)
        {
            return base.OnReconnected(request, connectionId);
        }


		/// 
		/// 查询数据
		/// 
		/// 
		/// 
		private List GetSummaryTableList(string userId)
        {
            var result = _base.Query($@"
			    select * from demo-data
					;
            ").ToList();
			return result;
        }
    }
}

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

using System.Collections.Generic;
using System.Linq;

namespace JiCai.Admin.Hubs.ConectionOperate
{
    /// 
    /// 连接管理
    /// 
    public class ConnectionManagement
    {
        /// 
        /// 用户连接集合
        /// 
        public static  List SignalRConns { get; set; } = new List();

        /// 
        /// 添加连接
        /// 
        /// 
        public  void AddConnInfo(SignalRConn conn)
        {
            SignalRConns.Add(conn);
        }

        /// 
        /// 删除连接
        /// 
        /// 
        public  void DelConnInfo(string connid)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.ConnectionId == connid);
            if (signalRConns != null)
            {
                SignalRConns.Remove(signalRConns);
            }
        }

        /// 
        /// 更新链接(老的链接不起作用了)
        /// 场景:客户端重连了,userid没变,但是connid变了
        /// 
        /// 用户id
        /// 新的链接id
        public  void UpdateConnInfo(string userId, string newConnsId)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.UserId.ToLower() == userId.ToLower());
            if (signalRConns != null)
            {
                signalRConns.ConnectionId = newConnsId;
            }
        }

        /// 
        /// 判断用户是否已经链接
        /// 
        /// 连接id
        /// 用户id
        /// 
        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
{
    /// 
    /// 连接
    /// 
    public class SignalRConn
    {
        /// 
        /// 系统用户id
        /// 
        public string UserId { get; set; }

        /// 
        /// SignleR链接Id(每次链接SignalR都会分配一个id)
        /// 
        public string ConnectionId { get; set; }
    }
}

2.2-客户端(JS)

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

下载地址(编码:yRLCRp81):yongteng.lanzoub.com/iXDlu1631ug…

密码:44x5

文件截图:
image

(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 += `
            `+obj[i].project_name+`
            `+ obj[i].zuori_sum+`
            `+ obj[i].jinri_sum+`
            `+ obj[i].qunian_sum+`
            `+ obj[i].jinnian_sum+`
            `+ obj[i].sum+`
            `+ obj[i].yikaipiao_sum+`
            `+ obj[i].weikaipiao_sum +`
            `+ obj[i].yishoupiao_sum +`
            `+ obj[i].weishoupiao_sum +`
            `+ obj[i].kehu_yinghuikuan_sum+`
            `+ obj[i].kehu_yihuikuan_sum+`
            `+ obj[i].kehu_weihuikuan_sum +`
            `+ obj[i].fuwu_yingfukuan_sum+`
            `+ obj[i].fuwu_yifukuan_sum+`
            `+ obj[i].fuwu_weifukuan_sum+`
            `
        }
        $("#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{
    
        .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);
        }
    
}

@section scripts{
    
    
    
}


    
    
    
        
            报表最新同步时间:
        
        
            
                
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                
                
                    
                        项目信息
                        销售情况
                        款项情况
                    
                    
                        项目名称
                        服务商名称
                        昨天订单总额
                        今天订单总额
                        去年订单总额
                        今年订单总额
                        订单总额

                        客户应回款总额
                        客户已回款总额
                        客户未回款总额
                        服务商应付款总额
                        服务商已付款总额
                        服务商未付款总额
                    
                
                
                
            
        
    


(4)效果展示:image.png

三、.NET Core WebAPI使用SignalR

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

3.1-服务端(.NET Core WebAPI)

1、右击项目,点击【管理NuGet程序包】安装SignalR。image.png

2、搜索【SignalR】,点击【安装】。image.pngimage.png

3、如果是.NET6以前的版本,在【Startup.cs】中配置:image.png如果是.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");
});

image.png

4、创建SignalR中心

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

    public class ChatHub:Hub
    {
        /// 
        /// 发送消息
        /// 
        /// 用户名
        /// 发送信息
        /// 
        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

版本截图:image.png

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

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

版本截图:image.png

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



  
  
  

    

      
        
          
             服务器信息
          
          
            
              
                
                  主机名称
                  {{serverInfo.HostName}}
                  系统名称
                  {{serverInfo.OSDescription}}
                
                
                  IP地址
                  {{serverInfo.IpAddress}}
                  操作系统
                  {{serverInfo.OperatingSystem}} {{serverInfo.OsArchitecture}}
                
              
            
          
        
      


      
        
          
             应用信息
          
          
            
              
                
                  .NET Core版本
                  {{serverInfo.FrameworkDescription}}
                  内存占用
                  {{serverInfo.MemoryFootprint}}
                
                
                  环境变量
                  {{serverInfo.EnvironmentName}}
                  项目路径
                  {{serverInfo.ContentRootPath}}
                
              
            
          
        
      

      
        
          
             磁盘状态
          
          
            
              
                
                  磁盘名称
                  盘符路径
                  文件系统
                  盘符类型
                  总大小
                  已用大小
                  可用大小
                  已使用率
                
              
              
                
                  {{ sysFile.DiskName }}
                  {{ sysFile.RootPath }}
                  {{ sysFile.DriveType }}
                  {{ sysFile.FileSystem }}
                  {{ sysFile.TotalSize }}
                  {{ sysFile.UseSize }}
                  {{ sysFile.ResidueSize }}
                  
                
              
            
          
        
      




      
        
          
             状态
          
          
            
              
                CPU使用率
                
                  
                
          
                {{ cpuInfo.ProcessorCount }} 核心
              

              
              
                内存使用率
                
                  
                
                {{ memoryInfo.UsedPhysicalMemory }} / {{ memoryInfo.TotalPhysicalMemory }}
              


              
                网络监控上传
                
                  
                    
                      {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}
                    
                  
                
                 {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}/S
              


              
                网络监控下载
                
                  
                    
                      {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}
                    
                  
                
                 {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}/S
              
            
          
        
      




      
        
            
              
                
                   CPU使用率监控
                
                
                  
                
              
            
            
              
                
                   内存使用率监控
                
                
                  
                
              
            
          
      

    
  



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(
  {
    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(
  {
    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');



.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;
  }
}

(5)效果展示:image.png

版权声明:本文为原创文章,版权归 [西瓜程序猿] 所有,转载请注明出处,有任何疑问请私信咨询。

原文链接:juejin.cn/spost/72697…

相关文章

如何删除WordPress中的所有评论
检查WordPress服务器磁盘使用情况的7种简便方法(查找大文件和数据)
如何更改WordPress常量FS_METHOD
如何为区块编辑器、Elementor等构建WordPress文章模板
如何彻底地删除WordPress主题及相关内容
如何使用WordPress搭建一个内网

发布评论