SignalR

SignalR 是微软开发的一个 实时通信库,用于在 .NET 应用中实现服务器与客户端(如网页、桌面 App)之间的双向、实时数据传输。 它的核心价值在于简化实时通信开发,无需手动处理底层的 WebSocket 协议、轮询等复杂逻辑,让开发者专注于业务功能。

核心特点:

  • 自动适配传输方式:优先使用高效的 WebSocket 协议,若环境不支持(如旧浏览器、网络限制),会自动降级为 Server-Sent Events、长轮询等方式。
  • 双向通信:服务器可主动向客户端推送数据(如实时通知、聊天消息),客户端也能实时向服务器发送请求,打破传统 HTTP “请求 - 响应” 的单向模式。
  • 跨平台兼容:支持多种客户端,包括 JavaScript(网页)、.NET(桌面 / 移动)、Java(Android)等,可用于构建全平台实时应用

如何引入

SignalR功能包含在Aegis.SignalR的Nuget包中,在引入之后请确认Component.deps.json配置文件中的ServicesMiddlewares节点中含有SignalR

image.png|1000

请确保在appsettings.json或者加载的配置文件中包含SignalR节点的配置,具体参考如下

{
  "SignalR": {
    //支持分布式的连接字符串
    "DistributedConnection": "",
    //开放的传输方式(WebSockets、ServerSentEvents、LongPolling),多个用逗号隔开,为空则全部开放
    "TransportType": "WebSockets,ServerSentEvents,LongPolling"
  }
}

说明

只要继承了Hub类,就会自动注入到当前应用中。

使用方法

SignalR的服务类

在Api项目下的Hubs文件夹中新建xxHub类,这个就是SignalR Hub的服务中心

image.png|400

public class xxHub : Hub
{
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="request">消息</param>
    public async Task SendMessage(MessageRequest request)
    {
        var message = $"Hello {request.Message}";

        await Clients.All.SendAsync("ReceiveMessage", new MessageResponse
        {
            Message = message
        });
    }
}

以上示例代码就是一个接收和发送消息的例子,方法说明:

  • SendMessage就是开放给客户端调用的方法,建议所有的方法都使用Request类来接收参数
  • Clients.All.SendAsync("ReceiveMessage", 参数)这个就是让所有连接的客户端接收名为ReceiveMessage的消息,参数也建议用类来包装

    • 发送方式有以下三种:
      • Clients.All:向所有连接的客户端发送消息
      • Clients.Caller:向调用了中心方法的客户端发送消息
      • Clients.Others:向所有连接的客户端发送消息(调用了方法的客户端除外)
    • 强类型Hub:为了防止SendAsync("ReceiveMessage")时,方法名出现拼写错误或缺失,强烈建议使用强类型Hub

        public interface IChatClient
        {
            Task ReceiveMessage(MessageResponse response);
        }
      
        public class xxHub : Hub<IChatClient>
        {
            /// <summary>
            /// 发送消息
            /// </summary>
            /// <param name="request">消息</param>
            public async Task SendMessage(MessageRequest request)
            {
                var message = $"Hello {request.Message}";
      
                //直接调用方法 ReceiveMessage
                await Clients.All.ReceiveMessage(new MessageResponse
                {
                    Message = message
                });
            }
        }
      
  • 向后兼容性:用类来包装入参和出参,是为了确保向后兼容性,具体可参考 https://learn.microsoft.com/zh-cn/aspnet/core/signalr/api-design?view=aspnetcore-6.0

认证鉴权重写

public class xxHub : Hub
{
    /// <summary>
    /// 建立连接
    /// </summary>
    /// <returns></returns>
    public override Task OnConnectedAsync()
    {
        Console.WriteLine($"{Context.ConnectionId}-建立连接");
        return base.OnConnectedAsync();
    }

    /// <summary>
    /// 断开连接
    /// </summary>
    /// <param name="exception">错误原因</param>
    /// <returns></returns>
    public override Task OnDisconnectedAsync(Exception? exception)
    {
        Console.WriteLine($"{Context.ConnectionId}-断开连接");
        return base.OnDisconnectedAsync(exception);
    }
}

以上示例代码是Hub类连接时和断开时会执行的方法,是虚方法,可按需决定是否需要重写

在Hub外部使用

在实际的业务使用中,可能也会存在在Controller中需要发送消息的情况,那么可参考下面的示例。具体可参考: https://learn.microsoft.com/zh-cn/aspnet/core/signalr/hubcontext?view=aspnetcore-6.0

public class HomeController : Controller
{
    //未使用强类型
    private readonly IHubContext<xxHub> _hubContext;
    //使用强类型
    public readonly IHubContext<xxHub, IChatClient> _chatHubContext

    public HomeController(IHubContext<xxHub> hubContext, IHubContext<xxHub, IChatClient> chatHubContext)
    {
        _hubContext = hubContext;
        _chatHubContext = chatHubContext;
    }

    //未使用强类型
    public async Task<IActionResult> SendMessage1()
    {
        await _hubContext.Clients.All.SendAsync("ReceiveMessage", new MessageResponse 
        {
            Message = "Hello World"
        });

        return SuccessResult("OK");
    }

    //使用强类型
    public async Task<IActionResult> SendMessage2()
    {
        await _hubContext.Clients.All.ReceiveMessage(new MessageResponse 
        {
            Message = "Hello World"
        });

        return SuccessResult("OK");
    }
}

results matching ""

    No results matching ""