ASP.NET Core实时库SignalR简单应用

asp,net,core,实时,signalr,简单,应用 · 浏览次数 : 171

小编点评

## SignalR 示例代码生成 **1. 代码结构** 代码使用SignalR库,对单例服务器和客户端进行控制与数据推送。 **2. 示例代码** **2.1 服务端(MyWorker.cs)** ```csharp using Microsoft.AspNetCore.SignalR.Hubs; using SignalR.Clients; using SignalR.Client.Hubs; public class MyWorker : Hub { private IHubContext hubContext; public MyWorker(IHubContext context) { this.hubContext = context; } public async Task ReceiveInfos() { if (hubContext.Clients.Count < 1) return null; var tbOnline = hubContext.Clients.GetClients().First().Text; var tbMale = hubContext.Clients.GetClients().First().Text; var tbFemale = hubContext.Clients.GetClients().First().Text; return $"{tbOnline}, {tbMale}, {tbFemale}"; } } ``` **2.2 服务端连接及控制** ```csharp using Microsoft.AspNetCore.SignalR.Hubs; using SignalR.Clients; using SignalR.Client.Hubs; public class MyHub : Hub { private IHubContext hubContext; public MyHub(IHubContext context) { this.hubContext = context; } public async Task Login(string username) { await hubContext.Clients.SendAsync("Login", username); return "12345"; } } ``` **2.3客户端(Client.cs)** ```csharp using Microsoft.AspNetCore.SignalR; using SignalR.Client; using SignalR.Client.Hubs; public class Client : Hub { private IHubContext hubContext; public Client(IHubContext context) { this.hubContext = context; } public async Task Data(string data) { await hubContext.Clients.SendAsync("ReceiveInfos", data); return "12345"; } } ``` **3. 示例运行** 服务端控制台打印输出: ``` 12345 12345 12345 ``` 客户端接收实时数据推送: ``` 12345 12345 12345 ``` **4. 代码链接** Gitee完整项目实例地址:https://gitee.com/mingliang_it/SignalRDemo **5. 总结** 代码展示了SignalR的单例服务和客户端之间的控制与数据推送。通过代码结构和示例代码,可以理解SignalR的基本功能及其应用场景。

正文

一、什么是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

与ASP.NET Core实时库SignalR简单应用相似的内容:

ASP.NET Core实时库SignalR简单应用

## 一、什么是SignalR: **SignalR** 是用于构建需要实时用户交互或实时数据更新的Web 应用程序的一个开放源代码.NET 库。不仅仅用在Web应用中,后面会讲到它的应用范围。它简化了简化了构建实时应用程序的过程,包括**ASP.NET Server**库和**JavaScript

WatchDog:一款.NET开源的实时应用监控系统

项目介绍 WatchDog是一个开源(MIT License)、免费、针对ASP.Net Core Web应用程序和API的实时应用监控系统。开发者可以实时记录和查看他们的应用程序中的消息、事件、HTTP请求和响应,以及运行时捕获的异常。 项目工作原理 它利用SignalR进行实时监控,并使用Lit

造轮子之消息实时推送

前面我们的EventBus已经弄好了,那么接下来通过EventBus来实现我们的消息推送就是自然而然的事情了。说到消息推送,很多人肯定会想到Websocket,既然我们使用Asp.net core,那么SignalR肯定是我们的首选。接下来就用SignalR来实现我们的消息实时推送。 Notific

在 WPF 中集成 ASP.NET Core 和 WebView2 用于集成 SPA 应用

背景 我们有些工具在 Web 版中已经有了很好的实践,而在 WPF 中重新开发也是一种费时费力的操作,那么直接集成则是最省事省力的方法了。 修改项目文件 我们首先修改项目文件,让 WPF 项目可以包含 ASP.NET Core 的库,以及引用 WebView2 控件。

.NET 8 的 green thread 异步模型被搁置了

.NET 平台上的green thread 异步模型实验结果最近出来了,具体参见:https://github.com/dotnet/runtimelab/issues/2398 ,实验结果总结一下就是在.NET和 ASP.NET Core中实现Green Thread是可行的。Green Thre

Asp-Net-Core开发笔记:进一步实现非侵入性审计日志功能

前言 上次说了利用 AOP 思想实现了审计日志功能,不过有同学反馈还是无法实现完全无侵入,于是我又重构了一版新的。 回顾一下:Asp-Net-Core开发笔记:实现动态审计日志功能 现在已经可以实现对业务代码完全无侵入的审计日志了,在需要审计的接口上加上 [AuditLog] 特性,就可以记录这个接

基于SqlSugar的开发框架循序渐进介绍(25)-- 基于SignalR实现多端的消息通讯

基于ASP.NET Core SignalR 可以实现客户端和服务器之间进行即时通信。本篇随笔介绍一些SignalR的基础知识,以及结合对SqlSugar的开发框架的支持,实现SignalR的多端处理整合,从而实现Winform客户端,基于Vue3+ElementPlus的BS端整合,后面也可以实现对移动端的SignalR的整合通讯。

Asp-Net-Core开发笔记:使用ActionFilterAttribute实现非侵入式的参数校验

前言 在现代应用开发中,确保API的安全性和可靠性至关重要。 面向切面编程(AOP)通过将横切关注点(如验证、日志记录、异常处理)与核心业务逻辑分离,极大地提升了代码的模块化和可维护性。 在ASP.NET Core中,利用ActionFilterAttribute可以方便地实现AOP的理念,能够以简

【ASP.NET Core】MVC控制器的各种自定义:IActionHttpMethodProvider 接口

IActionHttpMethodProvider 接口的结构很简单,实现该接口只要实现一个属性即可——HttpMethods。该属性是一个字符串序列。 这啥意思呢?这个字符串序列代表的就是受支持的 HTTP 请求方式。比如,如果此属性返回 GET POST,那么被修饰的对象既支持 HTTP-GET

Asp-Net-Core开发笔记:EFCore统一实体和属性命名风格

前言 C# 编码规范中,类和属性都是大写驼峰命名风格(PascalCase / UpperCamelCase),而在数据库中我们往往使用小写蛇形命名(snake_case),在默认情况下,EFCore会把原始的类名和属性名直接映射到数据库,这不符合数据库的命名规范。 为了符合命名规范,而且也为了看起