本文就简单使用 往前端页面推送消息

SignalR 是什么

SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式.

SignalR基于这三种技术构建, 抽象于它们之上, 它让你更好的关注业务问题而不是底层传输技术问题.

SignalR这个框架分服务器端和客户端, 服务器端支持ASP.NET Core 和 ASP.NET; 而客户端除了支持浏览器里的javascript以外, 也支持其它类型的客户端, 例如桌面应用.

对于.NET开发者的福音,.NET平台为我们提供了一种简洁高效智能的实时信息交互技术->SignalR,它集成了上述数种技术,并能根据配置自动或手动选择其最佳应用

可以用SignalR做什么?

  • SignalR可用于将任何类型的"实时"web 功能添加到 ASP.NET 应用程序。 比如最常用的即时消息、聊天。 只要用户刷新 web 页面以查看新数据或页面实现长轮询若要检索新数据,可以考虑对它使用 SignalR。 包括仪表板和监视应用程序,协作应用程序 (如同时进行编辑的文档),作业的进度更新到并实时窗体。

  • SignalR还可以用于需要高频率从服务器中更新的全新类型weB应用程序,例如在线聊天、实时游戏、天气、股票信息更新等实时应用程序。

  • SignalR 提供一个简单的 API,用于创建从服务器端.NET 代码中调用 JavaScript 函数在客户端浏览器 (和其他客户端平台) 的服务器到客户端的远程过程调用 (RPC)。 SignalR 还包括连接管理的 API (例如,连接和断开连接事件),并对连接进行分组。

回落机制

SignalR使用的三种底层传输技术分别是Web Socket, Server Sent Events 和 Long Polling.

其中Web Socket仅支持比较现代的浏览器, Web服务器也不能太老.

而Server Sent Events 情况可能好一点, 但是也存在同样的问题.

所以SignalR采用了回落机制, SignalR有能力去协商支持的传输类型.

Web Socket是最好的最有效的传输方式, 如果浏览器或Web服务器不支持它的话, 就会降级使用SSE, 实在不行就用Long Polling.

一旦建立连接, SignalR就会开始发送keep alive消息, 来检查连接是否还正常. 如果有问题, 就会抛出异常.

因为SignalR是抽象于三种传输方式的上层, 所以无论底层采用的哪种方式, SignalR的用法都是一样的.

SignalR默认采用这种回落机制来进行传输和连接.

但是也可以禁用回落机制, 只采用其中一种传输方式.

RPC

RPC (Remote Procedure Call). 它的优点就是可以像调用本地方法一样调用远程服务.

SignalR采用RPC范式来进行客户端与服务器端之间的通信.

SignalR利用底层传输来让服务器可以调用客户端的方法, 反之亦然, 这些方法可以带参数, 参数也可以是复杂对象, SignalR负责序列化和反序列化.

Hub

Hub是SignalR的一个组件, 它运行在ASP.NET Core应用里. 所以它是服务器端的一个类.

Hub使用RPC接受从客户端发来的消息, 也能把消息发送给客户端. 所以它就是一个通信用的Hub.

在ASP.NET Core里, 自己创建的Hub类需要继承于基类Hub.

在Hub类里面, 我们就可以调用所有客户端上的方法了. 同样客户端也可以调用Hub类里的方法.

这种Hub+RPC的方式还是非常适合实时场景的.

之前说过方法调用的时候可以传递复杂参数, SignalR可以将参数序列化和反序列化. 这些参数被序列化的格式叫做Hub 协议, 所以Hub协议就是一种用来序列化和反序列化的格式.

Hub协议的默认协议是JSON, 还支持另外一个协议是MessagePack. MessagePack是二进制格式的, 它比JSON更紧凑, 而且处理起来更简单快速, 因为它是二进制的.

此外, SignalR也可以扩展使用其它协议..

横向扩展

随着系统的运行, 有时您可能需要进行横向扩展. 就是应用运行在多个服务器上.

这时负载均衡器会保证每个进来的请求按照一定的逻辑分配到可能是不同的服务器上.

在使用Web Socket的时候, 没什么问题, 因为一旦Web Socket的连接建立, 就像在浏览器和那个服务器之间打开了隧道一样, 服务器是不会切换的.

但是如果使用Long Polling, 就可能有问题了, 因为使用Long Polling的情况下, 每次发送消息都是不同的请求, 而每次请求可能会到达不同的服务器. 不同的服务器可能不知道前一个服务器通信的内容, 这就会造成问题.

针对这个问题, 我们需要使用Sticky Sessions (粘性会话).

Sticky Sessions 貌似有很多中实现方式, 但是主要是下面要介绍的这种方式.

作为第一次请求的响应的一部分, 负载均衡器会在浏览器里面设置一个Cookie, 来表示使用过这个服务器. 在后续的请求里, 负载均衡器读取Cookie, 然后把请求分配给同一个服务器.

在ASP.NET Core 中使用SignalR

建立一个ServerHub, 继承于Hub:

public class ServerHub : Hub
{
/// <summary>
/// 已连接的用户信息
/// </summary>
public static List<UserModel> OnlineUser { get; set; } = new List<UserModel>(); private readonly ILogger<ServerHub> _logger;
private ISysUser _userService = null;
private readonly IHttpContextAccessor _accessor; public ServerHub(ISysUser user, ILogger<ServerHub> logger, IHttpContextAccessor accessor)
{
_userService = user;
_logger = logger;
_accessor = accessor;
} /// <summary>
/// 当连接成功时执行
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
string connId = Context.ConnectionId; _logger.LogWarning("SignalR已连接");
//验证Token
var token= _accessor.HttpContext.Request.Query["access_token"];
var user = JwtHelper.SerializeJwt(token);
_logger.LogWarning("SignalR已连接,用户名:" + user.UserName);
//连接用户 这里可以存在Redis
var model= new UserModel
{
ConnectionId = connId,
Token = token,
UserName = user.UserName
};
OnlineUser.Add(model);
//给当前的连接分组 可以进行同一组接收消息 也可以用Token获取机构权限
//await Groups.AddToGroupAsync(Context.ConnectionId, "测试组"); //给当前连接返回消息 .Clients可以发多个连接ID
Clients.Client(connId).SendAsync("ConnectResponse",
new ApiResult<UserModel>()
{
state=200,
data = model,
msg= user.UserName+"连接成功"
}); return base.OnConnectedAsync();
} /// <summary>
/// 当连接断开时的处理
/// </summary>
public override Task OnDisconnectedAsync(Exception exception)
{
string connId = Context.ConnectionId;
var model = OnlineUser.Find(u => u.ConnectionId == connId);
int count = OnlineUser.RemoveAll(u => u.ConnectionId == connId);
if (model != null)
{
//给当前分组发送消息 在一个分组就能接收到消息
//Clients.Group(model.GroupName).SendAsync("GetUsersResponse", result); //给当前连接返回消息 .Clients可以发多个连接ID
Clients.Client(connId).SendAsync("DisconnectResponse",
new ApiResult<bool>()
{
state = 1000,
data = true,
msg = "断开连接"
}); }
return base.OnDisconnectedAsync(exception);
} /// <summary>
/// 接受用户的数进行推送
/// </summary>
/// <returns></returns>
public async Task SendMessage(string user,string msg)
{
ApiResult<UserModel> result = new ApiResult<UserModel>();
result.data = new UserModel
{
ConnectionId = Context.ConnectionId,
Token = "",
UserName = user
};
result.state = 200;
result.msg = msg; //推送给所有连接ID的第一条数据
await Clients.Clients(OnlineUser.Select(q=>q.ConnectionId).ToList()).SendAsync("SendMessage", result);
} }

在Startup里注册SignalR:

注意 如果报跨域错误

endpoints.MapHub<ServerHub>("/serverHub").RequireCors(t => t.WithOrigins(new string[] { "http://localhost:8080" }).AllowAnyMethod().AllowAnyHeader().AllowCredentials());

我这里定时像前台推送数据  实际项目可以用MQ

/// <summary>
/// 利用Quartz定时推送假数据 一般是接受MQ推送的消息 或者从数据库 resis
/// </summary>
[DisallowConcurrentExecution]
public class SignalRJob : IJob
{
private readonly ILogger<SignalRJob> _logger;
private readonly IHubContext<ServerHub> _hubContext;
public SignalRJob(ILogger<SignalRJob> logger, IHubContext<ServerHub> hubContext)
{
_logger = logger;
_hubContext = hubContext;
} public async Task Execute(IJobExecutionContext context)
{
ApiResult<bool> result = new ApiResult<bool>();
if (ServerHub.OnlineUser.Count == 0)
{
result.data = false;
result.state = 1000;
result.msg = "没有连接用户";
}
else
{
result.data = true;
result.state = 200;
result.msg = "推送第一条数据";
} //推送给所有连接ID的第一条数据
await _hubContext.Clients.Clients(ServerHub.OnlineUser.Select(q => q.ConnectionId).ToList()).SendAsync("SendMessageResponse", result);
}
}

前端:

JS端

"use strict";

var connection = new signalR.HubConnectionBuilder()
.withUrl("https://localhost:44317/chatHub")
.withAutomaticReconnect() //断线自动重连
.build(); connection.start(); //自动重连成功后的处理
connection.onreconnected(connectionId => {
alert(connectionId);
}); //---消息---
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
}); connection.on("SendMessageResponse", function (res) {
if (res && res.status == 0) {
var li = document.createElement("li");
li.textContent = res.message;
document.getElementById("messagesList").appendChild(li);
} else {
alert(res.message);
}
});
//---消息--- //---登录---
document.getElementById("btnLogin").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("Login", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
}); connection.on("LoginResponse", function (res) {
if (res && res.status == 0) {
sessionStorage.setItem('curuser', res.data);
alert(res.message);
getUsers();
}
else {
alert('登录失败!');
}
});
//---登录--- //获取在线用户
function getUsers() {
connection.invoke("GetUsers").catch(function (err) {
return console.error(err.toString());
});
connection.on("GetUsersResponse", function (res) {
if (res && res.status == 0) {
var _lis = '<li>在线用户:</li>';
for (var i = 0; i < res.onlineUser.length; i++) {
_lis += `<li>${res.onlineUser[i].userName}</li>`;
}
document.getElementById("usersList").innerHTML = _lis;
}
});
}

VUE端

首先安装依赖包

npm install @microsoft/signalr

然后新建一个vue页面

<template>
<div class="hello">
<div id="message" v-html="remsg"></div>
<input type="text" placeholder="请输入用户名" v-model="user" />
<input type="text" placeholder="请输入内容" v-model="msg">
<button @click="handle">发送消息</button>
</div>
</template> <script> import * as signalR from "@microsoft/signalr"; let token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyTmFtZSI6ImFkbWluIiwiSUQiOiIyIiwiZXhwIjoxNTk5NjM3NjIxLCJpc3MiOiJuZXRsb2NrIiwiYXVkIjoibmV0bG9ja3MifQ.9T1zw2LaCx4enZLj5RCfxhJ85a169NPMqmW0n5OlzgI";
let hubUrl = "http://localhost:3906/serverHub"; //.net core 版本中默认不会自动重连,需手动调用 withAutomaticReconnect
const connection = new signalR.HubConnectionBuilder()
.withAutomaticReconnect()//断线自动重连
.withUrl(hubUrl,{ accessTokenFactory: () => token })//传递参数Query["access_token"]
.build(); //启动
connection.start().catch(err => {
console.log(err);
}); //自动重连成功后的处理
connection.onreconnected(connectionId => {
console.log(connectionId);
}); export default {
name: "First",
mounted() {
var _this = this; //调用后端方法 SendMessageResponse 接收定时数据
connection.on("SendMessageResponse", function(data) {
if(data.state==200)
_this.remsg = _this.remsg + "<br>" + "定时数据:" + data.msg;
}); //调用后端方法 SendMessage 接受自己人发送消息
connection.on("SendMessage", function(data) {
if(data.state==200)
_this.remsg = _this.remsg + "<br>" + data.data.userName + ":" + data.msg;
}); //调用后端方法 ConnectResponse 接收连接成功
connection.on("ConnectResponse", function(data) {
if(data.state==200)
_this.remsg = _this.remsg + "<br>" + "连接:" + data.msg;
}); },
data() {
return {
user: "",
msg: "",
remsg: ""
};
}, methods: {
handle: function() {
if(this.msg.trim()==""){
alert("不能发送空白消息");
return;
}
//调用后端方法 SendMessage 传入参数
connection.invoke("SendMessage", this.user, this.msg);
this.msg = "";
}
}
};
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#message { overflow-y:auto;
text-align: left;
border: #42b983 solid 1px;
height: 500px; } </style>

配置路由

最后的效果图

参考地址 https://www.cnblogs.com/shousiji/p/12737925.html

我的代码 https://files.cnblogs.com/files/netlock/SignalRDemo.rar

Asp.net Core3.1+Vue 使用SignalR推送数据的更多相关文章

  1. 使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能

    什么是 SignalR ASP.NET Core ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能. 实时 web 功能使服务器端代码可以立 ...

  2. SQL Server 2000向SQL Server 2008 R2推送数据

    [文章摘要]最近做的一个项目要获取存在于其他服务器的一些数据,为了安全起见,采用由其他“服务器”向我们服务器推送的方式实现.我们服务器使用的是SQL Server 2008 R2,其他“服务器”使用的 ...

  3. WebService推送数据,数据结构应该怎样定义?

    存放在Session有一些弊端,不能实时更新.server压力增大等... 要求:将从BO拿回来的数据存放在UI Cache里面,数据库更新了就通过RemoveCallback "告诉&qu ...

  4. java接口对接——调用别人接口推送数据

    实际开发中经常会遇到要和其他平台或系统对接的情况,实际操作就是互相调用别人的接口获取或者推送数据, 当我们调用别人接口推送数据时,需要对方给一个接口地址以及接口的规范文档,规范中要包括接口的明确入参及 ...

  5. Flume推送数据到SparkStreaming案例实战和内幕源码解密

    本期内容: 1. Flume on HDFS案例回顾 2. Flume推送数据到Spark Streaming实战 3. 原理绘图剖析 1. Flume on HDFS案例回顾 上节课要求大家自己安装 ...

  6. SuperSocket主动从服务器端推送数据到客户端

    关键字: 主动推送, 推送数据, 客户端推送, 获取Session, 发送数据, 回话快照 通过Session对象发送数据到客户端   前面已经说过,AppSession 代表了一个逻辑的 socke ...

  7. httpclient post推送数据

    客户端代码 /** * 从接口获取数据 * @param url 服务器接口地址 * @param json 传入的参数 若获取全部,此项为空 * @return 返回查询到的数据 * @throws ...

  8. hive向es推送数据

    第一步:首先要保证网络是通的,很多公司里子网遍布,要和运维和工程侧同事确认好网络是通的,es的地址可以通过curl es地址的方式测试一下. 第二步:下载需要的jar包,必须的是es-hadoop的包 ...

  9. ASP.NET SignaiR 实现消息的即时推送,并使用Push.js实现通知

    一.使用背景 1. SignalR是什么? ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指 ...

随机推荐

  1. Codeforces Round #672 (Div. 2) B. Rock and Lever题解(思维+位运算)

    题目链接 题目大意 给你一个长为n(n<=1e5)的数组,让你求有多少对a[i]和a[j] (i!=j)满足a[i]&a[j]>a[i]^a[j] 题目思路 这些有关位运算的题目肯 ...

  2. vm虚拟机安装centos7。克隆镜像以及快照

    为了方便下次安装配置,保存一篇安装centos的文章 https://blog.csdn.net/wsq119/article/details/80635558 步骤非常详细,一看就会. 这一篇是关于 ...

  3. 一千行MySQL命令

    基本操作 /* Windows服务 */ -- 启动MySQL net start mysql -- 创建Windows服务 sc create mysql binPath= mysqld_bin_p ...

  4. 第8.10节 使用__class__查看Python中实例对应的类

    一. 语法释义 __class__属性很简单,直接返回实例对应的类.语法如下: 实例. class 当不知道一个实例的类名又想对类的部分内容进行访问时可以使用__class__返回类. 注意:是返回实 ...

  5. Python函数学习遇到的问题

    Python函数的关键字参数 Python函数独立星号(*)分隔的命名关键字参数 Python函数中的位置参数 Python中对输入的可迭代对象元素排序的sorted函数 Python中函数的参数带星 ...

  6. PyQt(Python+Qt)学习随笔:树型部件QTreeWidget中当前列currentColumn和选中项selectedItems访问方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 当前列访问方法 树型部件QTreeWidget的currentColumn()方法返回当前项中得到焦 ...

  7. PyQt(Python+Qt)学习随笔:model/view架构中QTableView视图的数据无法显示问题

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在使用QTableView来显示如下数据时: 在model中插入数据是使用如下类似代码: for c ...

  8. PyQt学习随笔:PyQt中捕获键盘事件后获取具体按键值的方法

    在PyQt中,如果要捕获键盘事件的具体按键,可以通过重写组件对象的keyPressEvent方法或event方法来捕获具体的按键,推荐使用keyPressEvent方法,因为event方法是一个通用事 ...

  9. PyQt(Python+Qt)学习随笔:Action功能详解及Designer中的操作方法

    老猿Python博文目录 老猿Python博客地址 一.引言 Qt Designer中的部件栏并没Action相关的部件,Action可以在右侧的Action Editor中编辑,如图: 如果没有出现 ...

  10. PyQt(Python+Qt)学习随笔:Qt Designer中连接Action和槽函数

    在Designer中试了半天,终于找到了Action添加槽函数的方法,操作步骤: 在Designer右边界面中点击鼠标右键 确保信号/槽编辑被勾选,如图是未勾选的情况:. 勾选后会出现信号和槽的编辑界 ...