ASP.NET Core實時庫SignalR簡單應用

来源:https://www.cnblogs.com/wml-it/archive/2023/08/25/17656056.html
-Advertisement-
Play Games

## 一、什麼是SignalR: **SignalR** 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括**ASP.NET Server**庫和**JavaScript ...


一、什麼是SignalR:

SignalR 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括ASP.NET Server庫和JavaScript Client庫,以便管理Client與Server連接並將內容更新推送給Client。

SignalR可用於需要實時刷新獲取後臺數據的程式。常用的場景範圍有:社交應用程式、 多用戶游戲、 業務協作和新聞,天氣或財務更新應用程式等等。
image

二、關於WebSocket知識拓展:

在傳統的HTTP中,只能客戶端主動向伺服器端發起請求,伺服器端無法主動向客戶端發送消息。有的業務場景下,我們需要伺服器端主動向客戶端發送消息,比如Web聊天室、OA系統、站內消息等。

為了實現伺服器端向客戶端推送消息,在2008年誕生了WebSocket協議,並且該協議在2011年成為國際標準。目前所有的主流瀏覽器都已經支持WebSocket協議。WebSocket基於TCP(transmission control protocol,傳輸控制協議),支持二進位通信,因此通信效率非常高,它可以讓伺服器處理大量的併發WebSocket連接;WebSocket是雙工通信,因此伺服器可以高效地向客戶端推送消息。

三、建立SignalR服務端:

項目架構:

image

集線器服務定義:

  /// <summary>
    /// 定義集線器
    /// </summary>
    public class MyHub : Hub
    {
        /// <summary>
        /// 用戶字典
        /// </summary>
        private static Dictionary<string, string> dictUsers = new Dictionary<string, string>();

        /// <summary>
        /// 建立連接回調
        /// </summary>
        /// <returns></returns>
        public override Task OnConnectedAsync()
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已連接");
            return base.OnConnectedAsync();
        }

        /// <summary>
        /// 斷開連接回調
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        public override Task OnDisconnectedAsync(Exception? exception)
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已斷開");
            return base.OnDisconnectedAsync(exception);
        }


        /// <summary>
        /// 登錄功能,將用戶ID和ConntectionId關聯起來
        /// </summary>
        /// <param name="userId"></param>
        public void Login(string userId)
        {
            if (!dictUsers.ContainsKey(userId))
            {
                dictUsers[userId] = Context.ConnectionId;
            }
            Console.WriteLine($"{userId}登錄成功,ConnectionId={Context.ConnectionId}");
            //向所有用戶發送當前線上的用戶列表
            Clients.All.SendAsync("Users", dictUsers.Keys.ToList());
        }

        /// <summary>
        /// 退出功能,當客戶端退出時調用
        /// </summary>
        /// <param name="userId"></param>
        public void Logout(string userId)
        {
            if (dictUsers.ContainsKey(userId))
            {
                dictUsers.Remove(userId);
            }
            Console.WriteLine($"{userId}退出成功,ConnectionId={Context.ConnectionId}");
        }

    }

實時推送任務定義:

 /// <summary>
    /// 定義定時任務
    /// </summary>
    public class MyWorker
    {
        /// <summary>
        /// 單例
        /// </summary>
        public static MyWorker Instance;
        /// <summary>
        /// 鎖
        /// </summary>
        private static readonly object locker = new object();
        /// <summary>
        /// 集線器請求上下文
        /// </summary>
        private IHubContext<MyHub> context;
        /// <summary>
        /// 定時器
        /// </summary>
        public static System.Timers.Timer timer;

        /// <summary>
        /// 構造註入
        /// </summary>
        /// <param name="context"></param>
        public MyWorker(IHubContext<MyHub> context)
        {
            this.context = context;
            timer = new System.Timers.Timer(1000);//單位毫秒
            timer.Enabled = true;
            timer.AutoReset = true;//自動重新
            timer.Elapsed += Timer_Elapsed;
            timer.Start();
        }

        /// <summary>
        /// 時鐘到達事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            //模擬數據,一般情況下,從資料庫獲取,然後通知到客戶端
            Dictionary<string, object> data = new Dictionary<string, object>();
            var online = new Random().Next(0, 100);
            var male = Math.Floor(new Random().NextSingle() * online);
            var female = online - male;
            data["online"] = online;
            data["male"] = male;
            data["female"] = female;
            context.Clients.All.SendAsync("Data", data);
        }

        /// <summary>
        /// 單例註冊服務
        /// </summary>
        /// <param name="context"></param>
        public static void Register(IHubContext<MyHub> context)
        {
            if (Instance == null)
            {
                lock (locker)
                {
                    if (Instance == null)
                    {
                        Instance = new MyWorker(context);
                    }
                }
            }
        }
    }

全局註冊SignaIR服務:

using Microsoft.AspNetCore.SignalR;
using SignalRApi.Hubs;
using SignalRApi.Jobs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//1.添加註冊SignalR服務
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//使用路由
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthorization();

//在Use中註冊單例實例
app.Use(async (context, next) =>
{
    var hubContext = context.RequestServices
                            .GetRequiredService<IHubContext<MyHub>>();
    MyWorker.Register(hubContext);//調用靜態方法註冊

    if (next != null)
    {
        await next.Invoke();
    }
});
app.MapControllers();

//2.映射路由
app.UseEndpoints(endpoints => {
    endpoints.MapHub<MyHub>("/myhub");//啟用SignalR中間件,並且設置當客戶端通過SignalR請求“/myhub”這個路徑的時候,由ChatHub進行處理。
});


app.Run();

四、建立SignalR客戶端:

客戶端界面設計:

圖片

客戶端依賴:

客戶端下載安裝Nuget包:Microsoft.AspNetCore.SignalR.Client

dotnet add package Microsoft.AspNetCore.SignalR.Client --version 6.0.8

客戶端代碼—點擊事件定義:

        #region 點擊事件

        /// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btConnect_Click(object sender, EventArgs e)
        {
            //1.初始化
            InitInfo();
            //2.監聽
            Listen();
            //3.連接
            Link();
            //4.登錄
            Login();
        }

        /// <summary>
        ///斷開連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btDistinct_Click(object sender, EventArgs e)
        {
            hubConnection.InvokeAsync("Logout", "12345");
            _isDistinct = true;
        }
        #endregion

客戶端代碼—SignaIR連接與監聽定義:

        #region 連接和監聽
        /// <summary>
        /// 是否斷開連接
        /// </summary>
        private bool _isDistinct = false;

        /// <summary>
        /// 集線器連接對象
        /// </summary>
        private HubConnection hubConnection;

        /// <summary>
        /// 初始化Connection對象
        /// </summary>
        private void InitInfo()
        {
         hubConnection = new HubConnectionBuilder().WithUrl("http://localhost:5275/myhub").WithAutomaticReconnect().Build();//必須和在伺服器端MapHub註冊單例實例設置的路徑一致;
            hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
        }

        /// <summary>
        /// 監聽
        /// </summary>
        private void Listen()
        {
            hubConnection.On<Dictionary<string, object>>("Data", ReceiveInfos);
        }

        /// <summary>
        /// 連接
        /// </summary>
        private async void Link()
        {
            try
            {
                await hubConnection.StartAsync();//建立完成一個客戶端到集線器的連接。
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        /// <summary>
        /// /監聽事件
        /// </summary>
        /// <param name="data"></param>

        private void ReceiveInfos(Dictionary<string, object> data)
        {
            if (data == null || data.Count < 1 || _isDistinct)
            {
                return;
            }
            this.tbOnline.Text = data["online"]?.ToString();
            this.tbMale.Text = data["male"]?.ToString();
            this.tbFemale.Text = data["female"]?.ToString();
        }

       /// <summary>
       /// 登錄
       /// </summary>
        private void Login()
        {
            hubConnection.InvokeAsync("Login", "12345");
            _isDistinct =false;
        }
        #endregion

五、運行演示:

啟動服務:

image

啟動客戶端:

image

接收實時數據推送:

image

服務端控制台列印輸出:

image

源碼鏈接地址:

Gitee完整項目實例地址:

https://gitee.com/mingliang_it/SignalRDemo

本文來自博客園,作者:碼農阿亮,轉載請註明原文鏈接:https://www.cnblogs.com/wml-it/p/17656056.html


技術的發展日新月異,隨著時間推移,無法保證本博客所有內容的正確性。如有誤導,請大家見諒,歡迎評論區指正!
開源庫地址,歡迎點亮:
GitHub:https://github.com/ITMingliang
Gitee:   https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang

建群聲明: 本著技術在於分享,方便大家交流學習的初心,特此建立【編程內功修煉交流群】,為大家答疑解惑。熱烈歡迎各位愛交流學習的程式員進群,也希望進群的大佬能不吝分享自己遇到的技術問題和學習心得!進群方式:掃碼關註公眾號,後臺回覆【進群】。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前給大家推薦了很多後臺模版,有讀者希望推薦一些跟通用的好看組件,畢竟出了後臺還有很多其他場景嘛。所以,今天繼續給大家推薦一個廣受好評的UI組件庫:[**NextUI**](https://blog.didispace.com/tj-opensource-nextui/) ![NextUI](htt ...
  • 來源:https://heapdump.cn/article/1859160 通過這一個多月的努力,將 FullGC 從 40 次/天優化到近 10 天才觸發一次,而且 YoungGC 的時間也減少了一半以上,這麼大的優化,有必要記錄一下中間的調優過程。 對於 JVM 垃圾回收,之前一直都是處於理論 ...
  • ## 教程簡介 AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對 ...
  • 上一篇介紹了`DataFrame`的顯示參數,主要是對`DataFrame`中值進行調整。 本篇介紹`DataFrame`的顯示樣式的調整,顯示樣式主要是對錶格本身的調整,比如顏色,通過顏色可以突出顯示重要的值,觀察數據時可以更加高效的獲取主要信息。 下麵介紹一些針對單個數據和批量數據的樣式調整方式 ...
  • 在Java語言中,創建線程並不像創建對象一樣簡單。雖然只需要使用new Thread()即可創建線程,但實際上創建線程比創建對象複雜得多。創建對象只需在JVM的堆中分配記憶體,而創建線程需要調用操作系統內核的API,併為線程分配一系列資源,這個成本相對較高。因此,線程被視為重量級的對象,應儘量避免頻繁... ...
  • 最近閱讀了《ASP.NET Core 技術內幕與項目實戰——基於DDD與前後端分離》(作者楊中科)的第八章,對於Core入門的我來說體會頗深,整理相關筆記。 JWT:全稱“JSON web toke”,目前流行的跨域身份驗證解決方案; 標識框架(identity):由ASP.NET Core提供的框 ...
  • 在項目中經常會遇到類似如下要求的需求,創建允許自由拖動的控制項,這樣的需求可以使用WPF的裝飾器Adorner來實現。 一、什麼是裝飾器? 裝飾器是一種特殊類型的FrameworkElement,裝飾器始終呈現在被裝飾元素的頂部,用於向用戶提供可視化提示。裝飾器可以在不改變原有控制項結構的基礎上,將功能 ...
  • ## 一:背景 ### 1. 講故事 我發現有很多的 .NET程式員 寫了很多年的代碼都沒弄清楚什麼是 `虛擬地址`,更不用談什麼是 `物理地址` 以及Windows是如何實現地址映射的了?這一篇我們就來聊一聊這兩者之間的聯繫。 ## 二:地址映射研究 ### 1. 找虛擬地址 怎麼去找 `虛擬地址 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...