SignalR 实时通讯

news2025/1/16 18:53:57

SignalR 实时通讯

  • 1.SignalR
    • 1.1.SignalR 简介
    • 1.2.SignalR 功能
    • 1.3.传输
    • 1.4.中心
  • 2.服务器
    • 2.1.配置中心
    • 2.2.上下文对象
    • 2.3.客户端对象
    • 2.4.创建
    • 2.5.中心功能实现
  • 4.客户端
  • 6.案例演示(DotNet 客户端)

1.SignalR

1.1.SignalR 简介

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

SignalR 提供用于创建服务器到客户端远程过程调用 (RPC) 的 API。 RPC 从服务器端 .NET Core 代码调用客户端上的函数。 提供多个受支持的平台,其中每个平台都有各自的客户端 SDK。

与 ASP.NET 的其余部分一样,SignalR 是为实现高性能而构建的,也是市面上最快的实时框架之一。

跨服务器横向扩展,内置支持使用 Redis、SQL Server 或 Azure 服务总线来协调每个实例之间的消息。

1.2.SignalR 功能

  • 自动处理连接管理。
  • 同时向所有连接的客户端发送消息。 例如聊天室。
  • 向特定客户端或客户端组发送消息。
  • 对其进行缩放,以处理不断增加的流量。
  • SignalR 中心协议

1.3.传输

SignalR 支持以下用于处理实时通信的技术(优先选择 WebSockets):

  • WebSockets ⬇
  • Server-Sent Events ⬇
  • 长轮询

1.4.中心

SignalR 使用 中心 在客户端和服务器之间进行通信。

Hub 是一种高级管道,允许客户端和服务器相互调用方法。 SignalR 自动处理跨计算机边界的调度,并允许客户端调用服务器上的方法,反之亦然。 可以将强类型参数传递给方法,从而支持模型绑定。 SignalR 提供两个内置中心协议:基于 JSON 的文本协议和基于 MessagePack 的二进制协议。 与 ON 相比 JS,MessagePack 通常会创建较小的消息。 旧版浏览器必须支持 XHR 级别 2 才能提供 MessagePack 协议支持

  • 中心通过发送包含客户端方法的名称和参数的消息来调用客户端代码。
  • 作为方法参数发送的对象使用配置的协议进行反序列化。
  • 客户端尝试将名称与客户端代码中的方法匹配。
  • 当客户端找到匹配项时,它会调用该方法并将反序列化的参数数据传递给它。
  • 不支持 ECMAScript 6 以下的浏览器 ES6 to ES5

2.服务器

2.1.配置中心

中心 SignalR API 使连接的客户端能够在服务器上调用方法。 服务器定义从客户端调用的方法,客户端定义从服务器调用的方法。 SignalR 处理实现实时客户端到服务器和服务器到客户端通信所需的一切。

在 Program 中注册配置 SignalR 服务和 终结点

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR(); // 注册 SignalR 服务
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat"); // 配置 SignalR 终结点

app.Run();

2.2.上下文对象

  • 类 Hub 包含一个 Context 属性,该属性包含以下 属性方法
属性说明
ConnectionId获取连接的唯一 ID(由 SignalR 分配)。 每个连接都有一个连接 ID。
UserIdentifier获取用户标识符。 默认情况下,SignalR 使用与连接关联的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作为用户标识符。
User获取与当前用户关联的 ClaimsPrincipal。
Items获取可用于在此连接范围内共享数据的键/值集合。 数据可以存储在此集合中,会在不同的中心方法调用间为连接持久保存。
Features获取连接上可用的功能的集合。 目前,在大多数情况下不需要此集合,因此未对其进行详细记录。
ConnectionAborted获取一个 CancellationToken,它会在连接中止时发出通知。
方法说明
GetHttpContext返回 HttpContext 连接的 ;如果连接未与 HTTP 请求关联, null 则返回 。 对于 HTTP 连接,请使用此方法获取 HTTP 标头和查询字符串等信息。
Abort中止连接。

2.3.客户端对象

  • 类 Hub 包含一个 Clients 属性,该属性包含以下用于 服务器和客户端之间通信属性方法
属性说明
All对所有连接的客户端调用方法
Caller对调用了中心方法的客户端调用方法
Others对所有连接的客户端调用方法(调用了方法的客户端除外)
方法说明
AllExcept对所有连接的客户端调用方法(指定连接除外)
Client对连接的一个特定客户端调用方法
Clients对连接的多个特定客户端调用方法
Group对指定组中的所有连接调用方法
GroupExcept对指定组中的所有连接调用方法(指定连接除外)
Groups对多个连接组调用方法
OthersInGroup对一个连接组调用方法(不包括调用了中心方法的客户端)
User对与一个特定用户关联的所有连接调用方法
Users对与多个指定用户关联的所有连接调用方法

表中的每个属性或方法都返回具有 SendAsync 方法的对象。 方法 SendAsync 接收要调用的客户端方法的名称和任何参数。

和 方法返回 Client 的对象还包含方法 InvokeAsync,该方法可用于等待来自客户端的结果。Caller

2.4.创建

中心继承 Hub 类,可实现 弱类型中心强类型中心,这里只介绍强类型中心。

创建 IChatClient interface

public interface IChatClient
{
	Task ReceiveMessage(string user, string message);
}

实现强类型中心,Hub 类型为我们创建的 IChatClient interface

public class ChatHub : Hub<IChatClient>{}

2.5.中心功能实现

  • 向客户端发送消息
#region 连接处理

/// <summary>
/// 在连接上时
/// </summary>
/// <returns></returns>
public override async Task OnConnectedAsync()
{
    await SendMessageToCaller(Context.ConnectionId, "主服务连接成功!");
}

/// <summary>
/// 断开连接时
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception? exception)
{
    return base.OnDisconnectedAsync(exception);
}

#endregion

#region 消息发送

/// <summary>
/// 将消息发送到所有连接的客户端,不排除自己
/// </summary>
/// <param name="user"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 Clients.All 将消息发送到所有连接的客户端
/// </remarks>
/// <returns></returns>
public async Task SendMessage(string user, string message)
{
    await Clients.All.ReceiveMessage(user, message);
}

/// <summary>
/// 将消息发送到所有连接的客户端,排除自己
/// </summary>
/// <param name="user"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 Others 将消息发送到所有连接的客户端,排除自己
/// </remarks>
/// <returns></returns>
public async Task SendMessageToOthers(string user, string message)
{
    await Clients.Others.ReceiveMessage(user, message);
}

/// <summary>
/// 将消息发送到指定连接的客户端
/// </summary>
/// <param name="user1"></param>
/// <param name="user2"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 User 将消息发送到指定连接的客户端
/// </remarks>
/// <returns></returns>
public async Task SendMessageToUser(string user1, string user2, string message)
{
    await Clients.User(user1).ReceiveMessage(user2, message);
}

/// <summary>
/// 将消息发送回调用方
/// </summary>
/// <param name="user"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 Caller 将消息发送回调用方
/// </remarks>
/// <returns></returns>
public async Task SendMessageToCaller(string user, string message)
{
    await Clients.Caller.ReceiveMessage(user, message);
}

/// <summary>
/// 将消息发送给 GroupName 组中的所有客户端
/// </summary>
/// <param name="GroupName"></param>
/// <param name="user"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 Group 将消息发送给 GroupName 组中的所有客户端
/// </remarks>
/// <returns></returns>
public async Task SendMessageToGroup(string GroupName, string user, string message)
{
    await Clients.Group(GroupName).ReceiveMessage(user, message);
}

/// <summary>
/// 将消息发送给 GroupName 组中的所有客户端,排除自己
/// </summary>
/// <param name="GroupName"></param>
/// <param name="user"></param>
/// <param name="message"></param>
/// <remarks>
/// 使用 Group 将消息发送给 GroupName 组中的所有客户端,排除自己
/// </remarks>
/// <returns></returns>
public async Task SendMessageToOthersInGroup(string GroupName, string user, string message)
{
    await Clients.OthersInGroup(GroupName).ReceiveMessage(user, message);
}
#endregion

4.客户端

  • JavaScript 客户端

Internet Explorer 和其他旧版浏览器,当前版本及更高版本指的是浏览器的最新版本。

  • DotNet 客户端

.NET 客户端可在 ASP.NET Core 支持的任何平台上运行,具体版本号参考微软文档说明。

  • Java 客户端

需 Java 8 或更高版本。

6.案例演示(DotNet 客户端)

使用 WPF 客户端程序

 <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100*" MinHeight="120" />
        <RowDefinition Height="auto" />
        <RowDefinition Height="40*" MinHeight="130" />
    </Grid.RowDefinitions>

    <ListBox
        Name="listMessage"
        Background="{StaticResource DominantColor}"
        BorderThickness="0"
        ItemsSource="{Binding}">
        <ListBox.ItemTemplate>                        
            <DataTemplate>                            
                <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
                    <TextBlock VerticalAlignment="Center" Text="{Binding Message}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <GridSplitter
        Grid.Row="1"
        Height="1"
        HorizontalAlignment="Stretch"
        Background="{StaticResource BorderColor}" />

    <Grid Grid.Row="2" Margin="30,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="10" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="64" />
            </Grid.RowDefinitions>
            <TextBox
                x:Name="TxtNewContent"
                Grid.Row="0"
                Style="{StaticResource InputStyle}" />
            <Button
                Grid.Row="1"
                Click="Send_Click"
                Content="发送(S)"
                Style="{StaticResource SendButtonStyle}" />
        </Grid>
    </Grid>
</Grid>
/// <summary>
/// 连接中心
/// </summary>
private readonly HubConnection connection;

/// <summary>
/// 连接字符串
/// </summary>
readonly string connectionStr = "https://localhost:7067/chat";

/// <summary>
/// 消息通知
/// </summary>
ObservableCollection<MessageEntity> messages = new ObservableCollection<MessageEntity>();

public MainWindow()
{
    InitializeComponent();
    
    connection = new HubConnectionBuilder().WithUrl(connectionStr).WithAutomaticReconnect().Build();

    connection.Closed += async (error) =>
    {
        await Task.Delay(new Random().Next(0, 5) * 1000);
        await connection.StartAsync();
    };

    listMessage.ItemsSource = messages;

    Loaded += MainWindow_Loaded;
}

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    connection.On<string, string>("ReceiveMessage", (user, message) =>
    {
        this.Dispatcher.Invoke(() =>
        {
            messages.Add(new MessageEntity
            {
                Message = message,
                IsSend = false,
            });
        });
    });

    await StartReceiveMessage();
}

private async Task StartReceiveMessage()
{
    try
    {
        await connection.StartAsync();
        userId = connection.ConnectionId;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Send_Click(object sender, RoutedEventArgs e)
{
    try
    {
        await connection.InvokeAsync("SendMessageToOthers", userId, TxtNewContent.Text.Trim());
        TxtNewContent.Text = string.Empty;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

public class MessageEntity
{
    /// <summary>
        /// 消息
        /// </summary>
    public string Message { get; set; }
    /// <summary>
        /// 是否发送人
        /// </summary>
    public bool IsSend { get; set; }
}

程序演示
图中窗口使用 WindowChrome 控件自定义过,有需要可以去看【WPF】WindowChrome 自定义窗口完美实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/378902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内容感知、AI融合:让实景三维看山是山,看水是水

实景三维具备还原客观物理世界的优势性&#xff0c;但也正由于部分真实性的欠缺备受争议。这是因为传统的三维建模软件大多基于像元的匹配与计算的逻辑&#xff0c;对地物进行无差别的重建处理&#xff0c;最终生成的模型看起来扭曲怪异、残缺变形。常见的模型缺陷有&#xff1…

2022 OpenCV Spatial AI大赛前三名项目分享,开源、上手即用,优化了OAK智能双目相机的深度效果。

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

深圳居住证申领指南

打开广东政务服务网&#xff0c;在首页搜索【深圳经济特区居住证申领】在搜索结果中可以发现有如下链接&#xff0c;点击在线办理 会转到登陆界面&#xff0c;直接使用个人登录并用微信扫描登录 根据提示进行手机登录验证。 完成登录认证之后会自转到深圳经济特区居住证申领界…

二分查找由浅入深--算法--java

二分查找写在开头算法前提&#xff1a;算法逻辑算法实现简单实现leftright可能超过int表示的最大限度代码分析和变换更多需求&#xff1a;求索引最小的值java二分API应用基础题思考难度方法写在开头 二分查找应该是算比较简单的这种算法了&#xff0c;我本以为还可以。但有时候…

Word处理控件Aspose.Words功能演示:使用 Java 比较 MS Word 文档

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

动态规划初阶-爬楼梯问题

示例1&#xff1a; 输入&#xff1a;cost [10,15,20] 输出&#xff1a;15 解释&#xff1a;你将从下标为 1 的台阶开始。 - 支付 15 &#xff0c;向上爬两个台阶&#xff0c;到达楼梯顶部。 总花费为 15 。示例2&#xff1a; 输入&#xff1a;cost [1,100,1,1,1,100,1,1,10…

使用Docker安装MongoDB,整合SpringBoot

使用Docker安装MongoDB MongoDB 和 MySQL 都是常用的数据库管理系统&#xff0c;但它们的设计目标不同&#xff0c;因此在某些方面的性能表现也有所不同。 MongoDB 是一个文档型数据库&#xff0c;它采用了面向文档的数据模型&#xff0c;支持动态查询和索引&#xff0c;适合…

Docker部署实战

文章目录Docker部署应用准备制作容器镜像启动容器上传镜像docker exec数据卷&#xff08;Volume&#xff09;声明原理实践Docker部署 应用准备 这一次&#xff0c;我们来用 Docker 部署一个用 Python 编写的 Web 应用。这个应用的代码部分&#xff08;app.py&#xff09;非常…

【同步、共享和内容协作软件】上海道宁与​ownCloud让您的团队随时随地在任何设备上轻松处理数据

ownCloud是 一款开源文件同步、共享和 内容协作软件 可让团队随时随地 在任何设备上轻松处理数据 ownCloud开发并提供 用于内容协作的开源软件 使团队能够轻松地无缝 共享和处理文件 而无需考虑设备或位置 开发商介绍 ownCloud成立于2010年&#xff0c;是一个托管和同…

设计模式-笔记

文章目录七大原则单例模式桥模式 bridge观察者模式 observer责任链模式 Chain of Responsibility命令模式 Command迭代器模式 Iterator中介者模式 Mediator享元模式 Flyweight Pattern组合模式 composite装饰模式 Decorator外观模式 Facade简单工厂模式工厂方法模式工厂抽象模式…

Postgresql中的unlogged table

在PG中&#xff0c;有一种表的类型为unlogged table&#xff0c;名如其字&#xff0c;该种类型的表不会写入wal日志中&#xff0c;所以在写入的速度上比普通的堆表快很多&#xff0c;但是该表在数据库崩溃的时候&#xff0c;会被truncate,数据会丢失&#xff0c;而且该表也不支…

Leetcode21. 合并两个有序链表

一、题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4]输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

Java程序开发中如何使用lntelliJ IDEA?

完成了IDEA的安装与启动&#xff0c;下面使用IDEA创建一个Java程序&#xff0c;实现在控制台上打印HelloWorld!的功能&#xff0c;具体步骤如下。 1.创建Java项目 进入New Project界面后&#xff0c;单击New Project选项按钮创建新项目&#xff0c;弹出New Project对话框&…

【k8s】Kubernetes的学习(1.k8s概念和架构)

目录 1.首先要知道&#xff0c;Kubernetes为什么简称为k8s? 2.Kubernetes概述 2.1 kubernetes基本介绍 2.2 kubernetes的特性 2.3 kubernetes集群架构组件 2.3.1 Master (主控节点) 2.3.2 node (工作节点) 2.4 k8s核心概念 2.4.1 Pod 2.4.2 controller 2.4.3 Se…

操作系统权限提升(十九)之Linux提权-SUID提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 SUID提权 SUID介绍 SUID是一种特殊权限&#xff0c;设置了suid的程序文件&#xff0c;在用户执行该程序时&#xff0c;用户的权限是该程序文件属主的权限&#xff0c;例如程序文件的属主是root&#xff0c;那么执行该…

redux-saga

redux-saga 官网&#xff1a;About | Redux-Saga 中文网&#xff1a;自述 Redux-Saga redux-saga 是一个用于管理 异步获取数据(副作用) 的redux中间件&#xff1b;它的目标是让副作用管理更容易&#xff0c;执行更高效&#xff0c;测试更简单&#xff0c;处理故障时更容易… …

C#:Krypton控件使用方法详解(第十讲) ——kryptonColorButton

今天介绍的Krypton控件中的kryptonColorButton&#xff0c;下面介绍这个控件的外观属性&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;EmptyBorderColor属性&#xff1a;表示当所选颜色为空时&#x…

七、JUC并发工具

文章目录JUC并发工具CountDownLatch应用&源码分析CountDownLatch介绍CountDownLatch应用CountDownLatch源码分析有参构造await方法countDown方法CyclicBarrier应用&源码分析CyclicBarrier介绍CyclicBarrier应用CyclicBarrier源码分析CyclicBarrier的核心属性CyclicBarr…

echarts实现知识图谱,生产项目

echarts实现知识图谱&#xff0c;生产项目内容简介效果演示代码逻辑结束语内容简介 在实际生产项目中&#xff0c;需要对后端知识数据进行展示。需求如下&#xff1a; 点击节点可以展示与此节点相关的节点信息右键点击节点可以对节点的信息进行修改悬浮在节点上可以查看节点的…

通用人工智能(AGI):人工智能的下一个阶段

除了人工智能(AI)的改进和新应用之外&#xff0c;大多数人都认为&#xff0c;当通用人工智能(AGI)出现时&#xff0c;人工智能的下一次飞跃将发生。我们将AGI宽泛地定义为机器或计算机程序理解或学习人类可以完成的任何智力任务的假设能力。然而&#xff0c;对于何时以及如何实…