当前位置: 首页 > 工具软件 > Crypto-Signal > 使用案例 >

SignalR

韩单弓
2023-12-01

SignalR
SignalR 支持的平台  
ASP.NET Core SignalR 支持的平台  
SignalR 目录  
教程:ASP.NET Core SignalR 入门【添加 SignalR 客户端库】
ASP.NET Core SignalR 简介  (GitHub上的SignalR)   
在 SignalR 中使用中心 ASP.NET Core(上下文对象、Clients 对象)
ASP.NET Core SignalR 配置(配置持有者身份验证、配置客户端选项、配置超时和 keep-alive 选项、JSON/MessagePack 序列化选项、配置服务器选项)
ASP.NET Core SignalR JavaScript 客户端(连接到中心、错误处理和日志记录、重新连接客户端、自动重新连接)
ASP.NET Core SignalR 身份验证和授权  
Java 客户端 SignalR ASP.NET Core 
ASP.NET Core SignalR 客户端功能
在 ASP.NET Core 中使用流式处理 SignalR
ASP.NET SignalR 和 ASP.NET Core 之间的差异 SignalR
从向外发送邮件中心(Controller 调用 Hub)  
@microsoft/signalr - npm  
@apavelm/vue-signalr - npm    
signalr - npm  

必须使用 IIS8 及以上

JavaScript 连接 signalR

let connection = new signalR.HubConnectionBuilder()
    .withUrl("/pushHub", { accessTokenFactory: () => "Pcl9EWEa3sAY47VmvzJs59J86zoq_sTuBy09c7nCeFQ" })//ok
    .withUrl("/myhub", {
        accessTokenFactory: () => {
            // Get and return the access token.
            // This function can return a JavaScript Promise if asynchronous
            // logic is required to retrieve the access token.
        }
    })
    .build();

配置WebSockets连接方式

let connection = new signalR.HubConnectionBuilder()
    .withUrl("/myhub", {
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
    })
    .build();

JavaScript 连接 SignalR 完整示例【push.js】

//github地址:https://github.com/lianggx/Examples/tree/master/SignalR/Ron.SignalRLesson2/Ron.SignalRLesson1/Controllers

"use strict";

var connection = new signalR.HubConnectionBuilder()
    .withUrl("/pushHub")
    //.withUrl("/heartbeatHub")//心跳
    //.withUrl("/pushHub", { accessTokenFactory: () => "ekmoKVqJWgEkwPB4pqTkH_hjcGB7TSHJHoqciQobr5s" })
    //.withUrl("https://localhost:44383/pushHub")
    .configureLogging(signalR.LogLevel.Information)
    //.withAutomaticReconnect()
    //.withAutomaticReconnect([0, 3000, 6000, 12000, 18000, 24000])//以毫秒为单位
    .build();

//connection.keepAliveIntervalInMilliseconds = 15000 //15s
//connection.serverTimeoutInMilliseconds = 30000 //3s

connection.qs = { username: "qs" + "1111" };

connection.start()
    .then(function () {
        console.log("SignalR 连接成功");
    }).catch(function (err) {
        console.log("SignalR 连接失败");
        console.log(err);
    });

//async function start() {
//    try {
//        await connection.start();
//        console.log("connected");
//    } catch (err) {
//        console.log(err);
//        setTimeout(() => start(), 5000);
//    }
//};

connection.onclose(async () => {
    console.log('监听到链接关闭');
    connection.start()
        .then(function () {
            console.log("SignalR 连接成功");
        }).catch(function (err) {
            console.log("SignalR 连接失败");
            console.log(err);
        });
});

//连接时使用 onreconnected 更新 UI(onreconnecting 回调)
//connection.onreconnecting((error) => {
//    console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
//    const status = "Connection lost due to error " + error;
//    console.log(error);
//});

//onreconnected回调
//connection.onreconnected((connectionId) => {
//    console.log("Ln 26:" + connectionId);
//});

//绑定事件("Receive"和服务器端的Receive方法中的第一个参数一致)
connection.on("Receive", function (data) {
    debugger;
    console.log(data);
    var li = document.createElement("li");
    if (data.length > 0) {
        li = $(li).text(data[0].userName + ":" + data[0].content)
    }
    else {
        li = $(li).text(data.userName + ":" + data.content)
    }
    $("#msgList").append(li);
});

connection.on("ReceiveMessage", (data) => {
    console.log(data);//后台返回的数据,可以是json格式
});

connection.on("ReceivePublicMessage", function (data) {
    debugger;
    console.log("公共消息:" + data);
});

connection.on("ReceivePrivateMessage", function (data) {
    debugger;
    console.log("私有消息:" + data);
});

connection.on("ReceiveHeartbeat", function (data) {
    debugger;
    console.log(data);
    var li = document.createElement("li");
    if (data.length > 0) {
        li = $(li).text("接收心跳包:" + data[0].userName + ":" + data[0].content)
    }
    $("#msgList").append(li);
});

connection.on("HeartbeatAsync", function (data) {
    debugger;
    console.log(data);
    var li = document.createElement("li");
    li = $(li).text("接收心跳包:" + data.toString());
    $("#msgList").prepend(li);
});

$("#btnSend").on("click", () => {
    debugger;
    var userName = $("#userName").val();
    var content = $("#content").val();
    console.log(userName + ":" + content);
    connection.invoke("send", { "Type": 0, "UserName": userName, "Content": content });
});

$("#btnStop").on("click", () => {
    connection.stop();
});

$("#btnSendHeartbeat").on("click", () => {
    connection.invoke("SendHeartbeat");
});

$("#btnSendToUser").on("click", () => {
    var userName = $("#txtUserName").val();
    var usercontent = $("#txtUserContent").val();
    $.postJSON("api/v1/push/send_to_user", { Tyep: 1, UserName: userName, Content: usercontent }, (data) => {
        console.log(data);
    }, "json");
});

Java客户端添加header参数

HubConnection hubConnection = HubConnectionBuilder.create("https://example.com/myhub")
        .withHeader("Foo", "Bar")
        .shouldSkipNegotiate(true)
        .withHandshakeResponseTimeout(30*1000)
        .build();

Vue SignalR   
@microsoft/signalr - npm  
@apavelm/vue-signalr - npm    
signalr - npm  

npm install @aspnet/signalr

npm install @microsoft/signalr // 待测试

*、

<template>
   <el-row>
     <el-button type="success" @click="sendMsg">发送</el-button>
     <el-button type="info" @click="stop">停止</el-button>
   </el-row>
</template>

import * as signalR from '@aspnet/signalr'

export default {
  data() {
    return {
      user: {
        name: '',
        avatar: '',
      }
    }
  },
  methods: {
    sendMsg() {
      this.connection.invoke('SendHeartbeat').catch(function (err) {
        return console.error(err)
      })
    },
    stop() {
      this.connection.stop()
    },
    conn() {
      var $this = this
      var equipment = JSON.parse(localStorage.getItem('small_equipment'))
      console.log(`Ln 281 ${localStorage.getItem('small_equipment')};${equipment.equipment_code}`)
      this.connection = new signalR.HubConnectionBuilder()
      .withUrl("http://localhost:60548/pushHub?Bearer=123&password=abc", {
        skipNegotiation: true,                              /// 必填
        transport: signalR.HttpTransportType.WebSockets,    /// 必填
        // accessTokenFactory: () => 'Pcl9EWEa3sAY99J86'    /// 必填
        accessTokenFactory: () => {                         /// 必填
          // 访问令牌
          // 必填,后端获取context.Request.Query["access_token"]
          // Get and return the access token.
          // This function can return a JavaScript Promise if asynchronous
          // logic is required to retrieve the access token.
        }
      })
      // .configureLogging(signalR.LogLevel.Information)
      .withAutomaticReconnect() // 自动重新连接
      .build()

      // this.connection.keepAliveIntervalInMilliseconds = 15000 //15s
      // this.connection.serverTimeoutInMilliseconds = 30000 //3s

      this.connection.on("Receive", function(message) {
        var msg = JSON.parse(message)
        var u = JSON.parse(msg.content)

        $this.user.name = u.real_name
        $this.user.avatar = u.avatar

        var arrR = []
        arrR.splice(0, 0, message)
        arrR.push('5')
        console.log(`Ln 133 ${JSON.stringify(message)}`)
        console.log(`Ln 138 ${arrR}`)

        // var url= "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=1&text=" + encodeURI(message) 
        // new Audio(url).play()
      })

      this.connection.onclose(async () => {
        var d = new Date()
        var hour = (d.getHours() < 10) ? ("0" + d.getHours()) : d.getHours()
		var minute = (d.getMinutes() < 10) ? ("0" + d.getMinutes()) : d.getMinutes()
        var second = (d.getSeconds() < 10) ? ("0" + d.getSeconds()) : d.getSeconds()
        
        if(this.connection.state === signalR.HubConnectionState.Disconnected){
          console.log(`Ln 46 state:${this.connection.state}`)
          setInterval(() => {
            if($this.connection.state == 0){
              $this.conn()
            }
          }, 3000)
        }
        console.log(`监听到链接关闭 ${hour}:${minute}:${second}`)
      })

      this.connection.start()
        .then(function () {
          console.log("SignalR 连接成功")
        }).catch(function (err) {
          console.log("SignalR 连接失败")
          console.log(err)
        })
    }
  }
}

.NET Core(PushHub.cs)

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using NurseCall.CoreLibrary;
using NurseCall.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NurseCall.WebAPI
{
    public class PushHub : Hub
    {
        private static volatile PushHub instance = null;
        private static readonly object lockHelper = new object();

        private IHttpContextAccessor _accessor;
        public Dictionary<string, List<string>> UserList { get; set; } = new Dictionary<string, List<string>>();

        /// <summary>
        /// 单体模式返回当前类的实例
        /// </summary>
        /// <returns></returns>
        public static PushHub GetInstance()
        {
            if (instance == null)
            {
                lock (lockHelper)
                {
                    if (instance == null)
                    {
                        instance = new PushHub();
                    }
                }
            }
            return instance;
        }

        public PushHub() { }

        public PushHub(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }
       
        /// <summary>
        /// 客户端连接到集线器时执行操作
        /// </summary>
        /// <returns></returns>
        public override async Task OnConnectedAsync()
        {
            var dic = _accessor.HttpContext.Request.Headers;
            var token = dic["Authorization"].FirstOrDefault();
            LogUtils.GetInstance().Info($"Ln 144 有人进来了:{token},{this.Context.ConnectionId}");

            var bearer = Convert.ToString(_accessor.HttpContext.Request.Query["Bearer"]);
            var access_token = Convert.ToString(_accessor.HttpContext.Request.Query["access_token"]);
            var password = Convert.ToString(_accessor.HttpContext.Request.Query["password"]);

            var userName = this.Context.User.Identity.Name;
            if (string.IsNullOrEmpty(userName))
                userName = "admin";

            var user = VerificationToken(token);
            if (user != null)
            {
                var userCode = user.data.user_code;
                userName = user.data.user_name;
                var connectionId = this.Context.ConnectionId;
                if (!UserList.ContainsKey(userCode))
                {
                    UserList[userCode] = new List<string>();
                    UserList[userCode].Add(connectionId);
                }
                else if (!UserList[userCode].Contains(connectionId))
                {
                    UserList[userCode].Add(connectionId);
                }
                LogUtils.GetInstance().Info($"Ln 166 有人进来了:{userCode},{userName},{this.Context.ConnectionId}");
                await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
                await base.OnConnectedAsync();
            }
            else
            {
                LogUtils.GetInstance().Info($"Ln 172 有人进来了:非法用户{token},{this.Context.ConnectionId}");
            }
        }

        /// <summary>
        /// 客户端断开连接时执行操作
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var dic = _accessor.HttpContext.Request.Headers;
            //var token = dic["Authorization"].FirstOrDefault();
            await base.OnDisconnectedAsync(exception);
        }
    }
}

.NET Core(HeartbeatHub.cs)心跳

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebAPI
{
    public class HeartbeatHub : Hub<IHeartbeat>
    {
        public async Task Send(PushMessage data)
        {
            string aa = "12";
            await Clients.All.RecvAsync(data);
        }

        public async Task SendToUser(PushMessage data)
        {
            await Clients.Client("").SendToUser(data);
        }

        public async Task Login(PushMessage data)
        {
            string aa = "12";
            await Clients.All.ReceiveLogin(data);
        }

        public async Task Logoff(PushMessage data)
        {
            string aa = "12";
            await Clients.All.ReceiveLogoff(data);
        }

        public async Task SendHeartbeat(PushMessage data)
        {
            string aa = "12";
            await Clients.All.ReceiveHeartbeat(data);
        }

        public override async Task OnConnectedAsync()
        {
            //Console.WriteLine("游客[{0}]进入了聊天室", this.Context.ConnectionId);
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            //Console.WriteLine("游客[{0}]离开了聊天室", this.Context.ConnectionId);
            await base.OnDisconnectedAsync(exception);
        }
    }

    public interface IHeartbeat
    {
        Task SendToUser(PushMessage data);

        Task RecvAsync(object arg);

        Task ReceiveLogin(object arg);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        Task ReceiveLogoff(object arg);

        /// <summary>
        /// 接收客户端心跳包(前端响应方法名称)
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        Task ReceiveHeartbeat(object arg);

        /// <summary>
        /// 给所有客户端发送一个心跳包(前端响应方法名称)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        Task HeartbeatAsync(int data);
    }
}

.NET Core(Startup.cs)

namespace WebAPI
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        readonly string AllowSpecificOrigins = "AllowSpecificOrigins";

        public void ConfigureServices(IServiceCollection services)
        {
            #region 跨域访问
            var urls = Configuration["AppSettings:Cores"].Split(',');
            services.AddCors(options =>
            {
                options.AddPolicy(AllowSpecificOrigins, builder =>
                {
                    builder.WithOrigins(urls).AllowAnyMethod().AllowAnyHeader();
                });
            });
            #endregion

            #region SignalR
            services.AddSignalR(hubOptions =>
            {
                hubOptions.EnableDetailedErrors = true;
                //客户端发保持连接请求到服务端最长间隔,默认30秒,改成4分钟,网页需跟着设置connection.keepAliveIntervalInMilliseconds = 12e4;即2分钟
                //hubOptions.ClientTimeoutInterval = TimeSpan.FromMinutes(4);
                //服务端发保持连接请求到客户端间隔,默认15秒,改成2分钟,网页需跟着设置connection.serverTimeoutInMilliseconds = 24e4;即4分钟
                //hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(1);

                //hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(15);
                //hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
            }).AddJsonProtocol(options =>
            {
                //不更改属性名称的大小写
                //options.PayloadSerializerOptions.PropertyNamingPolicy = null;
            });
            services.AddSingleton<PushHub>();
            services.AddSingleton<HeartbeatHub>();
            #endregion
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseCors(AllowSpecificOrigins);
            //app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<PushHub>("/pushHub", options => { });
                endpoints.MapHub<HeartbeatHub>("/heartbeatHub", options => { });//心跳包
            });
            app.Use(async (context, next) =>
            {
                var accessToken = context.Request.Query["access_token"];
                if (context.Request.Query.TryGetValue("Bearer", out var token))
                    context.Request.Headers.Add("Authorization", $"Bearer {token}");
                await next();
            });
        }
    }
}

*、群聊,群发,组,groupName

using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using System.Threading.Tasks;

namespace NurseCall.WebAPI.Hubs
{
    public class GroupHub : Hub
    {
        public GroupHub()
        { }

        /// <summary>
        /// 添加组,根据客户端的ConnectionId,添加到组名为 groupName 的组中去
        /// </summary>
        /// <param name="groupName"></param>
        /// <returns></returns>
        public async Task AddToGroupAsync(string groupName)
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
        }

        /// <summary>
        /// 添加组,根据客户端的ConnectionId,添加到组名为 groupName 的组中去
        /// </summary>
        /// <returns></returns>
        public async Task Join(string groupName) => await Groups.AddToGroupAsync(Context.ConnectionId, groupName);

        /// <summary>
        /// 从 groupName 的组中,去除对应的客户端的ConnectionId
        /// </summary>
        /// <param name="groupName"></param>
        /// <returns></returns>
        public async Task RemoveFromGroupAsync(string groupName)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
        }

        /// <summary>
        /// 从 groupName 的组中,去除对应的客户端的ConnectionId
        /// </summary>
        /// <returns></returns>
        public async Task Remove(string groupName) => await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);

        /// <summary>
        /// 在 groupName 的组中,所有的客户端可以接收到服务端发送的消息
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendToGroupAsync(string groupName, PushMessage data)
        {
            await Clients.Group(groupName).SendAsync(groupName, JsonConvert.SerializeObject(data));
        }

        /// <summary>
        /// 在 groupName 的组中,所有的客户端可以接收到服务端发送的消息
        /// </summary>
        /// <returns></returns>
        public async Task Message() => await Clients.Groups("testGroup").SendAsync("groupMessages", "属于 testGroup 才能接收到数据");
    }
}


*、
*、
*、
*、

 类似资料: