SignalR の動作: .NET Framework および .NET Core で SignalR を使用する方法

公式 Web サイトのドキュメント:ASP.NET Core SignalR の概要 | Microsoft Learn

SignalR オープン ソース コード:SignalR · GitHub

目次

1. SignalR の概要

1.1-SignalR の概要

1.2-SignalR の適用

2. .NET Framework は SignalR を使用します

2.1 サーバー (.NET Framework MVC)

2.2-クライアント(JS)

3. .NET Core WebAPI は SignalR を使用します

3.1 サーバー (.NET Core WebAPI)

3.2-クライアント (Vue3+Vite+TS)


多くの友人が、「フロントエンドとバックエンドの分離プロジェクトでは、バックエンドは .NET Core で、フロントエンドは Vue です。SignalR を使用するにはどうすればよいですか?」と尋ねました。フロントエンドとバックエンドが分離されていないプロジェクトで、.NET Framework MVC プロジェクトで SignalR テクノロジを使用するにはどうすればよいですか?それでは以下の記事をご覧ください!この記事では、実際のプロジェクトでの SignalR の適用と、.NET Framework および .NET Core での SignalR の使用方法を主に紹介します。

1. SignalR の概要

1.1-SignalR の概要

ASP.NET Core SignalR は、アプリケーションへのリアルタイム Web 機能の追加を簡素化するために使用できるオープン ソース ライブラリです。リアルタイム Web 機能により、サーバー側のコードがコンテンツをクライアントにプッシュできるようになります。

1.2-SignalR の適用

  • サーバーからの高頻度の更新を必要とするアプリケーション: ゲーム、ソーシャル ネットワーク、投票、オークション、地図、GPS リファレンスなど。
  • ダッシュボードと監視アプリケーション: 企業ダッシュボード、即時販売最新情報、旅行アラートなど
  • コラボレーション アプリケーション: ホワイトボード アプリケーションやチーム会議ソフトウェアなど。
  • 通知アプリ: ソーシャル ネットワーク、電子メール、チャット、ゲーム、旅行アラート、その他のアプリの通知。

2. .NET Framework は SignalR を使用します

参考文献:

Owin 介绍:https://www.cnblogs.com/Pinapple/p/6721361.html

Owin 関連の事例:[Owin 学習シリーズ] 1. 最初の Owin プログラム_microsoft.owin_bifan546 のブログ - CSDN ブログ

2.1 サーバー (.NET Framework MVC)

(1) Signalrを使用するプロジェクトを選択し、[NuGetパッケージの管理]をクリックします。

 

(2) [SignalR] パッケージを検索し、クリックしてダウンロードします。

4 つは自動的にインストールされますが、それらの間には依存関係があることに注意してください。

(3) .NET Framework プラットフォームには、Owin 関連のパッケージを追加する必要があります。

OWIN は、Web サーバーとアプリケーション コンポーネント間の対話を定義する仕様です。この仕様の目的は、Microsoft .NET Framework に基づいて、Web サーバーとアプリケーション コンポーネントの広範で活発なエコシステムを開発することです。

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

(4) Webプロジェクト(使用するSign)に[Startup.cs]ファイルを作成します。

using SignalrDemo.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");
        }
    }
}

例えば:

(5) [Hubs] フォルダーを作成して、Hub 関連のファイルを特別に配置できます。 [Xigua プログラマー] これは [DemoHub.cs] という名前の Hub ファイルです。

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 SignalrDemo.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) [Xigua Programmer] は、Hubs/ConectionOperate フォルダー内に、すべての接続を管理するための [ConnectionManagement] ファイルを作成します。

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

namespace SignalrDemo.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 フォルダーに、SignalR とシステム ユーザー間の接続エンティティとして機能する [SignalRConn.cs] ファイルを作成します。

namespace SignalrDemo.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] をダウンロードします。ダウンロードにアクセスできます (失敗した場合は、私 [Xigua プログラマー] までご連絡ください)。

ダウンロード アドレス (エンコード: yRLCRp81): https://yongteng.lanzoub.com/iXDlu1631ugd
パスワード: 44x5

ファイルのスクリーンショット:

(2) signalRに接続するためのjsファイル[data_list_table_hub.js]を作成します。

// 连接服务
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)エフェクト表示:

3. .NET Core WebAPI は SignalR を使用します

シナリオ: プロジェクトでは、サーバーはクライアントに通知メッセージをアクティブに送信する必要があります。バックエンドは .NETCore を使用して実装され、フロントエンドはフロントエンドとバックエンドの分離モードで Vue3 ファミリ バケット プロジェクトを使用します。今回はSignalRを使った実装方法を中心にお話します。

3.1 サーバー (.NET Core WebAPI)

1. プロジェクトを右クリックし、[NuGet パッケージの管理] をクリックして SignalR をインストールします。

2. [SignalR]を検索し、[インストール]をクリックします。

3. .NET6 より前のバージョンの場合は、[Startup.cs] で設定します。

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

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

バージョンのスクリーンショット:

(2) 次に、ビジネス ロジック関連のコードをカプセル化する新しいファイルを作成します。 [Xigua Programmer] は [src/utils] ディレクトリに [signalr.ts] という名前のファイルを作成しました。これは js ファイルにすることもできます。独自のプロジェクトのニーズに応じて新しいファイルを作成することもできます。

コード:

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) 次に、ページを使用します。 [Xigua Programmer] ここではEchartsのチャート表示を例に説明します まず、Echartsパッケージをインストールします。

npm install ejectors --save

バージョンのスクリーンショット:

(4) 次に、次のコードをページに書き込み、必要に応じて特定のページ スタイルを変更および調整します。

<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)エフェクト表示:

著作権表示: この記事はオリジナル記事であり、著作権は [Xigua Programmer] に属します。転載する場合は出典を明記してください。ご質問がある場合は、プライベート メッセージを送信してください。

元のリンク:実際の SignalR: .NET Framework と .NET Core で SignalR を使用する方法_Xigua プログラマーのブログ - CSDN ブログ

おすすめ

転載: blog.csdn.net/2301_79251107/article/details/132429636