当前位置: 首页 > news >正文

.net 6 + vue3中使用SignaIR实现双向通信功能

目录

一、SignaIR 的介绍       

1.1、SignaIR 的协议类型

1.2、SignaIR 的使用场景

二、SignaIR 的使用

2.1、后端

2.1.1、安装 SignaIR 包

2.1.2、住入 SignaIR 依赖

2.1.3、创建 SignaIR 核心组件

2.1.4、数据更新触发服务器数据推送到客户端

2.2、前端

2.2.1、安装 SignaIR 库

2.2.2、引入 SignaIR 库

2.2.3、使用 SignaIR 库创建长连接

三、遇到的问题及解决方法


一、SignaIR 的介绍       

         SignaIR 是一个可跨平台的开源库,让客户端与服务器的通信变得简单,它允许服务器实时推送数据到客户端,使得客户端能够及时地收到服务器的数据并呈现给用户。

1.1、SignaIR 的协议类型

        WebSockets:是一种在单个TCP连接上进行全双工通信的协议,使得服务器和浏览器的通信更加简单,服务端可以主动发送信息。在建立连接后,服务器向客户端返回数据后连接不会断,会一直保持连接。

        Server-Sent Events:简称 SSE,是基于 HTTP协议的。客户端通过发送一个特殊的 HTTP GET请求到服务器请求中包含 Accept: text/event-stream 头,表明客户端期望接收 SSE 数据流,服务器响应后并建立了连接,会保持连接打开,服务器可以持续向客户端推送数据(数据是由一系列事件组成)。SSE 是单向通信的,只能由服务器向客户端发送数据。

        Long Polling(长轮询) :客户端会发送一个 HTTP 请求,服务器不会立即返回响应,一直保持连接,直到有数据更行时才返回响应,这样减少了每次请求的次数。如果长时间没有数据。则会超出设置的超时时间,可以在超时前返回一个 null,让前端在次轮询。

SignalR会自动选择服务器和客户端能力范围内的最佳通信方式,当然也可以手动指定。

1.2、SignaIR 的使用场景

        SignaIR 的通信方式是为了能够及时地发现服务器有数据更新并展示给用户,一般用于在 网络聊天、实时状态更新、报警器、消息提醒、协作应用、游戏、看板等业务场景。

二、SignaIR 的使用

        在 .net 6 + vue3 的项目中使用 SignaIR 进行实时通信,需要在 .net 6、vue3中都引入 SignaIR 库。

2.1、后端

2.1.1、安装 SignaIR 包

在 NUGET 包管理器中下载 SignaIR 包。

Microsoft.AspNet.SignalR

2.1.2、住入 SignaIR 依赖

在 Program.cs文件中注入依赖。

builder.Services.AddSignalR();

2.1.3、创建 SignaIR 核心组件

        新建 Hubs文件夹,在文件夹中添加命名以 Hub结尾的类,用于处理客户端与服务器的通信。

public class GDPDateHub:Hub
{
    private readonly AppDbContext _context;
    public GDPDateHub(AppDbContext context)
    {
        _context = context;
    }
    /// <summary>
    /// 获取数据,用于第一次向客户端发送数据
    /// </summary>
    /// <returns></returns>
    public async Task GetGDPDate()
    {
        var gdpDate = await _context.GDPDatas.ToListAsync();
        // 定义一个消息名称 ReceiveGDPDate
        await Clients.All.SendAsync("ReceiveGDPDate", gdpDate);
    }
}

        使用 SignaIR与客户端进行通信时,与传统的 webapi接口的通信方式不同,SignaIR 会将数据从 SignaIR的核心组件 Hub 中通过请求管道返回给客户端,所以我们还需要在请求管道中注入核心组件的 SignaIR 依赖。

在 Program.cs文件中注入核心组件的 SignaIR 依赖。

// GDPDateHub:核心组件类型

//  "/gdphub" :核心组件的路由,可以把核心组件看作是一个 HTTP GET请求的 API 接口,客户端是通过这个路由来找到核心组件处理业务逻辑的。

app.MapHub<GDPDateHub>("/gdphub");

        有同学可能会问,如果我们有多个接口需要做实时通信怎么办?那还不好说吗,做添加几个核心组件,然后给每个核心组件注入核心组件的 SignaIR 依赖。

        还有同学要问,那如果我需要给这个请求添加认证和权限分配怎么办?这个问题我下期再讲,也可以学习: SignalR:身份认证

2.1.4、数据更新触发服务器数据推送到客户端

        在新增、修改和删除数据的接口中使用 _hubContext.Clients.All.SendAsync 来触发推送数据更新,SendAsync方法接收两个参数,第一个参数是触发消息推送的消息名称(与核心组件中的一致),第二个参数是推送到前端的数据。

[Route("api/[controller]")]
[ApiController]
public class GDPDataController : ControllerBase
{
    private readonly AppDbContext _context;
    private readonly IGDPDataRepository _dataRepository;
    private readonly IHubContext<GDPDateHub> _hubContext;
    public GDPDataController(AppDbContext context, IGDPDataRepository dataRepository, IHubContext<GDPDateHub> gDPDataRepository)
    {
        _context = context;
        _dataRepository = dataRepository;
        _hubContext = gDPDataRepository;
    }
    [HttpPost]
    public async Task<IActionResult> AddGDPDate([FromBody] GDPData gdpDate)
    {
        if(gdpDate == null)
        {
            return BadRequest("参数错误");
        }
        _context.GDPDatas.Add(gdpDate);
        await _context.SaveChangesAsync();
        // 触发向客户端推送数据,
        // ReceiveGDPDate:推送的消息名称,用来识别处理那个核心组件
        // await _dataRepository.GetGDPDatasAsync():推送的数据,我这里推送的是所有数据,可以根据业务需求推送最新数据。
        await _hubContext.Clients.All.SendAsync("ReceiveGDPDate",await _dataRepository.GetGDPDatasAsync()); 
        return Ok("创建成功");
    }
}

2.2、前端

        在vue3中使用 @microsoft/signalr 库创建 BubConnection 对象后, BubConnection 对象中的常用连接操作方法。

常用连接方法
功能方法描述案例
连接管理方法stop()用于停止当前SignaIR连接。调用该方法后,连接会进入Disconnecting状态,最终变为Disconnected状态。await  connection.stop();
消息发送方法invoke()用于调用服务器端 Hub 类中的方法,并可以传递参数。该方法返回一个Promise,可以使用await关键字等待服务器的响应。

await connection.invoke("方法名","参数值");

send()同样用于调用服务器后端 Hub 类中的方法,但不等待服务器的响应。返回一个Promise,当消息成功发送到服务器时,Promise会被解决。await  connection.send("方法名","参数值");
消息接收方法on()用于监听服务器端发送的特定消息。当服务器调用Clients.All.SendAsync等待方法消息时,客户端可以通过on方法监听该消息,并在接收到消息时执行响应的回调函数。connection.on("

ReceiveMessage

",(data) => {console.log("收到消息:", data);});
事件监听方法onreconnecting()用于监听连接重新建立的过程。当连接断开并重新连接时,会触发该事件的回调函数。connection.onreconnecting((err) => {console.log("正在连接,错误信息:",err);});
onreconnected()用于监听连接重新建立成功的事件。当连接重新建立成功后,会触发该事件的回调函数。connection.onreconnected((connectionId) => {console.log("重新连接成功,新连接ID:", onnectionId)});
onclose()用于监听连接关闭的事件。当连接关闭时,会触发该事件的回调函数。connection.onclose((err) => {console.log("连接已关闭,错误信息:", err);});

2.2.1、安装 SignaIR 库

cnpm install @microsoft/signalr

2.2.2、引入 SignaIR 库

import * as signaIR from '@microsoft/signalr';

2.2.3、使用 SignaIR 库创建长连接

指定使用 websocket 协议来建立长连接。我这里使用类型协商的方式会报错,提示协商失败,我就给它指定的使用 websocket 协议来建立长连接,如果想要通过类型协商的方式来创建连接,请看下面。

// 创建连接 
// withUrl() 指定要连接的后端 SignaIR Hub 的URL。
// build() 构建返回一个 HubConnection 对象。
const connection = new signaIR.HubConnectionBuilder()
    .withUrl("http://localhost:5251/gdphub", {
        skipNegotiation: true, // 忽略协商,直接使用 WebSocket
        transport: signaIR.HttpTransportType.WebSockets
    })
    .build();
const getGDPDates = async () => {
    var result;
    try {
        // connection.start() 启动与后端的连接
        await connection.start();
        if (connection.state === signaIR.HubConnectionState.Connected) {
            // connection.on 监听服务器发送的 ReceiveGDPDate 消息,并在连接收到消息时执行相应的回调函数。
            // 注册接收消息事件,ReceiveGDPDate:服务端推送消息的方法名,必须与服务器端的核心组件中的消息名一致。
            connection.on('ReceiveGDPDate', (data) => {
                result = data;
            });
        }
        // 调用服务器中的 Hub 核心组件的 GetGDPDate 方法
        await connection.invoke("GetGDPDate"); // GetGDPDate:服务端方法名,必须与服务器端的核心组件中的方法名一致。
    } catch (error) {
        console.log(error);
    }
    return result;
};

不指定 SignaIR 的通信类型,通过类型协商的方法来常见连接。

// 创建连接
const connection = new signaIR.HubConnectionBuilder()
    .withUrl("http://localhost:5251/gdphub")
    .build();

三、遇到的问题及解决方法

        遇到问题及解决方法的参考文章:.NET 6使用 SignaIR 遇到的问题及解决方法

        这里我主要介绍如何查看 websocket 是否连接成功。

判断依据
枚举值数值状态
Disconnected0连接已经关闭,处于关闭状态
Connecting1正在建立连接
Connected2连接已建立,可以进行通信
Reconnecting3

正在重新建立连接

Disconnecting4连接正在进行关闭

方法一:在前端的控制台中输入:new WebSocket(地址)

可以查看地址是否正确。

看到图中返回的信息,url 中有个 ws,这个 ws又是什么鬼东西?没见过?

Websocket使用 ws 或 wss 的统一资源标志符,类似于 HTTP 或 HTTPS ,其中 wss 表示在 TLS 之上的 Websocket ,相当于 HTTPS 了。如:

ws:localhost:5251/gdphub

wss:localhost:5251/gdphub

默认情况下,Websocket 的 ws 协议使用 80 端口;运行在TLS之上时,wss 协议默认使用 443 端口。其实说白了,wss 就是 ws 基于 SSL 的安全传输,与 HTTPS 一样样的道理。

如果你的网站是 HTTPS 协议的,那你就不能使用 ws了,浏览器会 block 掉连接,和 HTTPS 下不允许 HTTP 请求一样,这时候我们就需要使用 wss了。

方法二:在 await connection.start() 后面打印 connection.state。

await connection.start();
console.log("SignalR 连接已建立");
console.log(connection.state);


好记性不如烂笔头,在学习的路上留下痕迹。希望能给你带来帮助,也期待你的点赞和评论。

若有不足之处,还请斧正。

http://www.dtcms.com/a/107612.html

相关文章:

  • 界面架构 - MVVM (Qt)
  • 准确--回顾B站 “713“ 大规模服务不可用事故
  • 合并有序链表
  • 致敬生物信息学先驱:玛格丽特·戴霍夫(Margaret Dayhoff,1925-1983)
  • 多分类交叉熵
  • AIP-211 授权检查
  • transformer结构原理
  • Unity开发——Destory延迟销毁导致异常的处理
  • 关于 数据库 UNION 和 UNION ALL 的使用,以及 分库分表环境下多表数据组合后的排序和分页问题的解决方案 的详细说明,并以表格总结关键内容
  • 【黑科技护航安全】分布式光纤测温:让隐患无处可藏
  • Vite 内联 CSS 和 JS 的解决方案
  • 机器学习的一百个概念(7)独热编码
  • 大文件上传源码,支持单个大文件与多个大文件
  • 【三层架构有哪些?】
  • 函数fcntl(File Control)
  • spring AOP 事务 过滤器(Filter)与拦截器(Interceptor)
  • java项目分享-分布式电商项目附软件链接
  • C语言变长数组(VLA)详解:灵活处理动态数据的利器
  • 【大模型系列篇】大模型基建工程:使用 FastAPI 构建 MCP 服务器
  • 智能语音识别工具开发手记
  • Java 枚举类 Key-Value 映射的几种实现方式及最佳实践
  • 【Framework-Client系列】UIGenerate介绍
  • MTK AWB 色温曲线的进一步猜想
  • 时尚优雅奢华品牌包装徽标设计衬线英文字体安装包 Kagea – Luxury Women Ligature Font
  • 司南评测集社区 3 月上新一览!
  • 启服云商标管理软件:律师事务所的高效之选
  • 数据结构优化DP总结
  • SvelteKit 最新中文文档教程(17)—— 仅服务端模块和快照
  • 智能配电箱:重塑未来电力管理的核心枢纽
  • 大模型显卡网络