首页 > 编程语言 >《ASP.NET Core技术内幕与项目实战》精简集-高级组件4.4:消息推送SignalR

《ASP.NET Core技术内幕与项目实战》精简集-高级组件4.4:消息推送SignalR

时间:2022-11-08 22:57:48浏览次数:73  
标签:Core ASP HTTP 集线器 SignalR 连接 服务端 客户端

本节内容,部分为补充内容。主要NuGet包:

  • Microsoft.AspNetCore.SignalR.Client(BlazorWASM的SignalR客户端)
  • Microsoft.AspNetCore.SignalR.StackExchangeRedis(使用Redis部署分布式SignalR)

 

一、SignalR基本使用

SignalR使用集线器Hub类,连接服务端和客户端,实现服务端与客户端,以及客户端之间的双向通讯,关系如下,服务端<->集线器<->客户端。首先,在服务端配置SignalR服务,同时创建集线器类,包括具体响应逻辑和发送消息命令;然后,在客户端连接集线器,监听消息,并发送请求。

1、服务端操作Asp.net Core

 

//第一步:在Hubs文件夹中,创建集线器类
//ChatRoomHub.cs,继承自Hub类
public class ChatRoomHub : Hub 
{
    //集线器方法,客户端可以请求调用
    public Task SendPublicMessage(string user, string message)
    {
        string connId = Context.ConnectionId; //获取连接客户端ID
        string msg = $"{connId}/{user}: {message}";
        //向所有连接的客户端发送消息,消息名“ReceivePublicMessage”,消息内容msg
        return Clients.All.SendAsync("ReceivePublicMessage", msg);
    }
}


//第二步:配置SignalR服务
//program.cs
var builder = WebApplication.CreateBuilder(args);
......
//注册SignalR服务
builder.Services.AddSignalR();

//注册跨域服务,并将Blazor项目的URL设置为可信任
string[] corsUrls = new[] { "https://localhost:7042", "http://localhost:5042" };
builder.Services.AddCors(opts =>
{
    opts.AddDefaultPolicy(builder =>
    {        builder.WithOrigins(corsUrls).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
    });
});

var app = builder.Build();
......
//启用跨域中间件
app.UseCors();

app.UseHttpsRedirection();
......
//启用和配置SignalR中间件,当客户端访问/Hubs/ChatRoomHub时,使用ChatRoomHub进行处理
app.MapHub<ChatRoomHub>("/Hubs/ChatRoomHub");

app.MapControllers();
app.Run();

 

补充说明:

①Signal是对WebSocket的封装,基于TCP。相对于HTTP,①采用二进制通信,通讯效率高;②使用双工通信,所以服务器可以向客户端推送消息

②虽然Signal独立于HTTP,但一般将WebSocket服务端部署到HTTP服务器上,一是借助HTTP完成初次“握手”协商,二是共享HTTP服务器的端口

③本案例,使用前后端分离模式,客户端使用BlazorWASM,由于SignalR连接的初次握手需要借助HTTP,所以要启动跨域CORS支持

④Hub类的生命周期为瞬态,在集线器类中不要保持状态。Hub类还提供了两个虚方法OnConnectedAsync和OnDisconnectedAsync,可以在客户端连接或断开时执行逻辑

⑤SignalR提供了各种客户端SDK,用于客户端连接集线器,包括BalzorWASM、JavaScript、Java等

 

 

2、客户端操作Blazor Webassembly

//第一步:安装Nuget包,Microsoft.AspNetCore.SignalR.Client


//第二步:创建前端UI,模拟简单的聊天室,在同一页面,发送消息和接收消息
//Index.razor
<h3>User:</h3><input @bind = "userInput" />
<h3>Message:</h3><input @bind = "messageInput" />
<button @onclick = "Send" disabled="@(!IsConnected)">发送</button>
@foreach (var message in messages)
{
    <li>@message</li>
}

@code{
    private HubConnection? hubConnection; //集线器连接对象
    private string? userInput; //输入用户名
    private string? messageInput; //输入聊天信息
    private List<string> messages = new List<string>(); //聊天记录
    
    protected override async Task OnInitializedAsync()
    {
        //创建集线器连接
        hubConnection = new HubConnectionBuilder()
            .WithUrl("https://localhost:7110/Hubs/ChatRoomHub")
            .WithAutomaticReconnect()
            .Build();
        
        //监听集线器消息,将消息内容添加到messages集合中,并刷新
        hubConnection.On<string>("ReceivePublicMessage", msg =>
        {
            messages.Add(msg);
            StateHasChanged();
        });

        //开始连接集线器
        await hubConnection.StartAsync();
    }

    //如果已连接集线器,则调用集线器方法,发送信息
    private async Task Send()
    {
        if (hubConnection is not null)
            {
                await hubConnection.SendAsync("SendPublicMessage", userInput, messageInput);
            }
    }

    //判断客户端是否连接集线器
    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;
    
    //组件销毁时,断开集线器连接(销毁集线器连接对象)
    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}

 

 

二、除了可以在集线器中向客户端推送消息,还可以在控制器或托管服务中,调用集线器,向客户端推送信息

//第一步:服务端,在控制器中注入IHubContext<THub>服务,并发送消息
[Route("api/[controller]/[action]")]
[ApiController]
public class MyController : ControllerBase
{
    private readonly IHubContext<ChatRoomHub> hubContext;
    public MyController(IHubContext<ChatRoomHub> hubContext)
    {
        this.hubContext = hubContext;
    }

    [HttpGet]
    public async Task<ActionResult> SendControllerMessage(string msg)
    {
        await hubContext.Clients.All.SendAsync("ControllerMessage", msg);
        return Ok();
    }
}


//客户端,在前端中增加对消息“ControllerMessage”的监听
hubConnection.On<string>("ControllerMessage", msg =>
{
    messages.Add(msg);
    StateHasChanged();
});

 

 

 

三、SignalR连接过程,禁用协商

1、SignalR封装了Websocket、服务器发送事件、长轮询三种实现方式,优先顺序依次降低。连接时,客户端首先发出一个negotiate请求,协商使用哪种协议,这次协商过程称之为“握手”。确定好协议后,客户端按协商协议再次连接。

2、在协商过程中,服务器就会创建上下文信息,建立连接后,会继续使用这些上下文信息。如果有多台服务器,可能发生创建上下文信息的服务器和实际连接的服务器不一致情况。这种情况,一般使用“禁止协商”来解决,即不用协商过程,直接按Websocket连接,毕竟现代客户端绝大多数都支持Websocket协议

 

//客户端连接时,禁止协商设置
hubConnection = new HubConnectionBuilder()
    .WithUrl("https://localhost:7110/Hubs/ChatRoomHub",options =>
    {
        options.SkipNegotiation = true;
        options.Transports = HttpTransportType.WebSockets;
    })
    .WithAutomaticReconnect()
    .Build();

 

 

 

四、SignalR的分布式部署

 

如果SignalR的服务端,部署在多个服务器上,连接到不同服务器上的客户端,由于连接的是不同的集线器,所以没办法通讯。可以通过使用Redis作为中间层,解决数据同步的问题。首先安装Nuget包:Microsoft.AspNetCore.SignalR.StackExchangeRedis。然后在服务端的program.cs中设置SignalR

 

//AddStackExchangeRedis方法的第一个参数为Redis服务器地址,第一个参数配置当前应用程序的前缀
builder.Services.AddSignalR().AddStackExchangeRedis("127.0.0.1", opts =>
{
    opts.Configuraton.ChannelPrefix = "Test1_"
});

 

 

 

五、SignalR的身份认证及发送范围控制(略,详见认证和授权章节)

 

 

六、补充:几个常见通讯协议的区别和联系

1、TCP、HTTP和Websocket

  • TCP:在OSI模型有7层结构中,属于传输层协议,HTTP和Websocket的底层连接都基于TCP
  • HTTP:在OSI模型有7层结构中,属于应用层协议,基于TCP
  • Websocket:和HTTP一样,也属于应用层协议,也基于TCP

 

2、HTTP和Websocket的特点及区别:
1)HTTP

  • 基于请求响应模式,由客户端发起请求,服务端做出响应。
  • 无连接、无状态,即每次请求响应后,连接都会断开,且服务端不会记录
  • 以文本方式传输数据
  • 指定了80(HTTP)和443(HTTPS)端口,一般计算机不会限制,可以顺利通过防火墙
  • 有同源限制,即跨域问题

2)Websocket

  • 基于双工模式,客户端和服务端可以相互推送信息
  • 可以保持长连接,可以保存状态信息
  • 即可以传输文本,也可以传输二进制,且传输报文更轻量,所以性能更高效
  • 与HTTP协议有着良好的兼容性,一般将Websocket布置到HTTP服务器上,共享HTTP的端口,服务端和客户端初次握手阶段也使用HTTP协议
  • 没有同源限制,不存在跨域问题
  • 协议标识符为ws或wss(加密),网址也使用URL

 

3、HTTP1、HTTP2、HTTP3的区别
1)HTTP1的问题

  • 每个TCP连接只能处理一个请求,且浏览器限制连接数,所以会造成请求阻塞
  • 只能通过文本方式传输数据,性能开销较大

2)HTTP2的提升

  • 引用多路复用技术,一个TCP连接可以处理所有请求数据
  • 新的编码机制,传输数据被分割,且采用二进制,性能更高效
  • 服务端在客户端请求后,可以PUSH其它资源

3)HTTP3的提成

  • 使用QUIC,解决HTTP2可能存在的丢包重连的情况,而且可以缓存上下文,在HTTP2基础上,大大提升了性能和纠错能力

 

4、RPC:不是协议,而是一种远程调用方法(API)

  • 可以基于TCP协议,也可以基于HTTP协议,还可以自定义TCP协议
  • 基于二进制传输,且传输报文更轻量,性能高
  • 自带负载均衡,而HTTP需要借助Nginxt等
  • RPC因为传输效率高、性能消耗低等特点,常用于服务端之间相互调用
  • gRPC是由google开发的RPC框架,基于HTTP2协议,与语言无关,应用比较广泛

 

 

特别说明:
1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍

 

标签:Core,ASP,HTTP,集线器,SignalR,连接,服务端,客户端
From: https://www.cnblogs.com/functionMC/p/16867881.html

相关文章

  • dotnet Core 在linux 下设置成Service
    1、新建.service文件cd/etc/systemd/system//进入改目录touchCore.service//新建Core服务文件viCore.service//编辑2、插入下面代码注意自己的服务名,以及项......
  • 快速理解ASP.NET Core的认证与授权
      原文网址:https://blog.csdn.net/helendemeng/article/details/122352963ASP.NETCore的认证与授权已经不是什么新鲜事了,微软官方的文档对于如何在ASP.NETCore中......
  • EF Core级联保存时DbUpdateConcurrencyException报错异常
    出现改报错异常的原因是,EFCore不支持级联更新时添加新的子项!!!如果主体子项添加一个新内容,EFCore则认为这个内容原本已经存在了(实际是你新增的),只不过并发冲突中被其他进程......
  • .NetCore 接口接收图片文件并保存
    本文实例环境及版本.NetCore3.1publicIActionResultUploadFile([FromForm]IFormCollectionformCollection){if(formCollection==nu......
  • Entity Framework Core Migrations
    EntityFrameworkCoreMigrationsWhendevelopingapplications,themodelislikelytochangeoftenasnewrequirementscometolight.Thedatabaseneedstobe......
  • 重新整理 .net core 实践篇 ———— dotnet-dump [外篇]
    前言本文的上一篇为:https://www.cnblogs.com/aoximin/p/16861797.html该文为dotnet-dump和procdump的实战介绍一下。正文现在很多情况下去抓取dotnet运行的信息......
  • 解决 net core 3.x 跨域问题
    跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。以下几种情况是造成跨域的原因:域名相同,端口不......
  • 关于ASP.NET Core WebSocket实现集群的思考
    前言    提到WebSocket相信大家都听说过,它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议。在没有WebSocket之前只能通过......
  • ASP.NET Core教程-Configuration(配置)-Swagger
    更新记录转载请注明出处:2022年11月8日发布。2022年11月5日从笔记迁移到博客。注册服务在服务容器中注册服务,使用AddSwaggerGen()方法。builder.Services.AddSw......
  • 【EF Core 】实体跟踪与非跟踪查询 AsNoTracking()
    微软官方文档链接跟踪行为决定了EntityFrameworkCore是否将有关实体实例的信息保留在其更改跟踪器中。如果已跟踪某个实体,则该实体中检测到的任何更改都会在SaveCha......