signalr实时通讯

前言

我这里使用signalr
的动机是因为有些大屏数据和设备信息需要自动更新,当时我是使用的轮询的方式,当时轮询周所周知,再数据没有变化的情况下,是非常耗费资源的。闲暇的时候,更是不能使用轮询来造成额外的耗费。

正文

使用场景 使用SignalR与Asp.net Core通讯

1、signalR简介

signalR 是一个开源的库,跨平台;可以让web应用与其他应用通讯变得非常简单,WEB服务端可以实时的将内容推送给对应的客户端,客户端发送的信息也可以实时到其他客户端。
signalR 提供了一种远程过程调用(RPC)的方式,使得客户端可以调用服务端的方法,同样在服务端的方法中也能调用刻画短的方法。

1.1signalR的通信方式

SignalR 支持如下的方式实时通信:

websockets:

是一种在单个TCP连接上进行全双工通信的协议,使得服务器和浏览器的通信更加简单,服务端可以主动发送信息。

Server-Sent Events:

SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。websocket 是双向,而 SSE是单向的。

Long Polling(长轮询):

和传统的轮序原理一样,只是服务端不会每次都返回相应信息,只有有数据或超时了才会返回,从而减少了请求次数。

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

1.2 SignalR的应用场景

其实对于Web模式下的实时通信,SignalR用上试试
服务端主动推送信息;比如发送公告场景。
监控或看板数据实时显示;比如监控系统实时展示分布到各个客户端上的数据
服务端和客户端交互;比如客服系统的聊天场景。

2. 案例演示

这里我把SignalR的服务端寄宿在WebAPI项目中了,实际可以根据需要寄宿到对应的项目(窗体应用、后台服务),当然也可以单独为其创建一个项目,但代码编写都基本一样。

  • 创建一个WebAPI项目,引入对应的Nuget包
  • 编写自己的SignalR Hub
  • Hub就是一个类,只是里面编写的方法客户端可以远程调用到(原理后续咱们一起读读源码);同样在服务端也可以远程用客户端的方法,这样就使得实时通信变得简单便捷了。

dia

  • 在Startup.cs文件中注册相关服务及管道

在这里插入图片描述

//参数中的值一定要和以上设置的跨域名字一样
app.UseCors("SignalRCors");
//可以设置SignalR相关参数,这里设置地址
app.UseSignalR(routes => { routes.MapHub<LCHub>("/lchub/signalr"); });

业务API编写,推送消息
在这里插入图片描述

我这里前后端分离所以我需要关注跨域Startup.cs中进行注入和配置,在ConfigureServices中添加如下代码

//设置跨域问题  因为前后端分离可能不在统一站点部署,会出现跨域问题,这里进行跨域配置
 services.AddCors(options => {
        options.AddPolicy("SignalRCors", policy => policy.AllowAnyOrigin()
                                                         .AllowAnyHeader()
                                                         .AllowAnyMethod()
                                                         .AllowCredentials());

});

在这里插入图片描述
到这服务端的业务就写完了,接下来就是开始编写客户端。

2.2 JS客户端

Js客户端使用Vue组件,绑定数据方便;放在WebApi项目的wwwroot目录下,和WebAPI一起共用服务器启动,所以就不用考虑跨域问题。如果前端分开部署,需要在SignalR寄宿的项目中配置跨域。具体步骤如下:

  • 获取signalr封装好的js文件,开箱即用

npm init -y
npm install @microsoft/signalr(如果引用不成功可以尝试使用@aspnet/signalr)

vue中使用插件的方式(对,就是通过脚手架建立的在vscode 编辑)

import * as signalR from "@aspnet/signalr"
import axios from "axios"

// 先声明自己开发的插件对象 MyPlugin const MyPlugin={}
const signal = new signalR.HubConnectionBuilder()
  .withUrl(axios.defaults.baseURL+ "ChatHub/", {skipNegotiation: true,
  transport: signalR.HttpTransportType.WebSockets,})
  .configureLogging(signalR.LogLevel.Information)
  .build()

 const signalr = function () {
  var hub
  if (hub === undefined) {
    hub = signal
  }
  return hub
} 
//  自动重连
async function start () {
  try {
    await signal.start()
    console.log('connected')
  } catch (err) {
    console.log(err)
    setTimeout(() => start(), 5000)
  }
}

signal.onclose(async () => {
  await start()
}) 
export default {
  install: function (Vue) {
    Vue.prototype.signalr = signal
    console.log('signalr');
  }
}

在这里插入图片描述
启动signalr

  StartRun() {
      let thatis = this;

      console.log(thatis.signalr.connection.connectionState);
      //这里主要防止多次重连
      if (thatis.signalr.connection.connectionState == "2") {
        thatis.signalr.start().then(() => {
          console.log("链接");
        });
      }

      console.log(thatis.signalr.connection.connectionState);
    },
  },

监听来自于服务端的信息

onRun() {
      let thatis = this;

      thatis.signalr.off("ReceiveMessage");
      thatis.signalr.on("ReceiveMessage", function (user, message) {
        var ote = JSON.parse(message);
        thatis.circleData = ote.StocksNum;
   
        thatis.messageValue = ote;
        thatis.$Message.info(thatis.messageValue);
      });
      thatis.signalr.on("ListenStock", function (user, message) {
        var ote = JSON.parse(message);
        thatis.TodayStockMessage = ote.TodayStock;
       
      });
      thatis.signalr.on("ListenTask", function (user, message) {
        var ote = JSON.parse(message);
        ote.runtime.forEach((x) => {
          thatis.DeviceRunTime.push(x.runtime);
        });

        ote.num.forEach((x) => {
          if (x.type == "InBound") {
            thatis.InTodayAllStock = x.num;
          } else if (x.type == "OutBound") {
            thatis.OutTodayAllStock = x.num;
          }
        });
      });
    },

如果想要客户端给服务端发送信息只需要通过

 connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });

过来进行 发送信息

这里通过下面的代码获得客户端发送过来的信息

 public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }

由于我通过axios 来post ,所以这里signalr只是接受服务段信息

结语:目前的上面的代码已经基本实现前后端信息的交互。当然,signalr依然存在很多坑,例如调用的时候如果前端进行发布iis或者其他发布工具一定要开通websocket。第二个坑,就是signalr很容易多次start 导致发送的信息会变得重复,再刷新或者到其他界面会造成多次发送信息。或者就是连接断联重连问题,信号灯超时问题等等。
如果想要深入研究signalr可以访问官网访问地址
参考文章
https://www.cnblogs.com/vktun/p/7218151.html
https://blog.csdn.net/sD7O95O/article/details/100973025
https://www.cnblogs.com/zoe-zyq/p/12049307.html
https://blog.csdn.net/weixin_42776027/article/details/117608853
https://www.cnblogs.com/liuxiaobo93/p/11244762.html
http://www.manongjc.com/detail/14-oprlmugzmvesbbf.html
https://blog.csdn.net/chinahuyong/article/details/98873807

当你还不能对自己说今天学到了什么东西时,你就不要去睡觉。——利希顿堡

  • 11
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值