什么是 SignalR ASP.NET Core

ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能。 实时 web 功能使服务器端代码可以立即将内容推送到客户端。

SignalR ASP.NET Core可以做什么

• 需要从服务器进行高频率更新的应用。 示例包括游戏、社交网络、投票、拍卖、地图和 GPS 应用。
• 仪表板和监视应用。 示例包括公司仪表板、即时销售更新或旅行警报。
• 协作应用。 协作应用的示例包括白板应用和团队会议软件。
• 需要通知的应用。 社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需使用通知。

SignalR  ASP.NET Core特色

• 自动处理连接管理。
• 可将消息同时发送到所有连接的客户端。
• 可向特定客户端或客户端组发送消息。
• 可缩放以处理不断增加的流量。
• SignalR采用rpc来进行客户端与服务器端之间的通信。
• SignalR会自动选择服务器和客户端的最佳传输方法(WebSockets、Server-Sent事件、长轮询)SignalR可以根据当前浏览器所支持的协议来选择最优的连接方式,从而可以让我们把更多的精力放在业务上而不是底层传输技术上。

哪些浏览器支持SignalR  ASP.NET Core

Apple Safari(包含IOS端)、Google Chrome(包括 Android端)、Microsoft Edge、Mozilla Firefox等主流浏览器都支持SignalR  ASP.NET Core。

本次我们将实现一个通过SignalR来简单实现一个后台实时推送数据给Echarts来展示图表的功能

首先我们新建一个ASP.NET Core 3.1的web应用

随后我们引用SignalR  ASP.NET Core、Jquery和Echarts的客户端库

在项目中我们新建以下目录

Class、HubInterface、Hubs

接着我们在Pages目录下新建如下目录

echarts

在Shared目录中新建一个Razor布局页(_LayoutEcharts.cshtml)

在echarts目录中新建一个Razor页面(Index.cshtml)

在Class目录中新建一个类(ClientMessageModel.cs)

在HubInterface目录中新建一个接口(IChatClient.cs)

在Hub目录中新建一个类(ChatHub.cs)

我们先实现后台逻辑代码,随后在编写前端交互代码。

在IChatClient.cs中,我们主要是定义统一的服务端调用客户端方法的统一方法名(防止每次都要手动输入调用方法是出现失误而导致调用失败的低级错误)

namespace signalr.HubInterface
{
public interface IChatClient
{
/// <summary>
/// 客户端接收数据触发函数名
/// </summary>
/// <param name="clientMessageModel">消息实体类</param>
/// <returns></returns>
Task ReceiveMessage(ClientMessageModel clientMessageModel);
/// <summary>
/// Echart接收数据触发函数名
/// </summary>
/// <param name="data">JSON格式的可以被Echarts识别的data数据</param>
/// <returns></returns>
Task EchartsMessage(Array data);
/// <summary>
/// 客户端获取自己登录后的UID
/// </summary>
/// <param name="clientMessageModel">消息实体类</param>
/// <returns></returns>
Task GetMyId(ClientMessageModel clientMessageModel);
}
}

ClientMessageModel.cs中,我们主要定义的是序列化后的交互用的实体类

namespace signalr.Class
{
/// <summary>
/// 服务端发送给客户端的信息
/// </summary>
[Serializable]
public class ClientMessageModel
{
/// <summary>
/// 接收用户编号
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 组编号
/// </summary>
public string GroupName { get; set; }
/// <summary>
/// 发送的内容
/// </summary>
public string Context { get; set; } }
}

在ChatHub.cs中,主要是实现SignalR集线器的核心功能,用来处理客户端<==>服务器交互代码。在这里我们继承了Hub<T>的方法,集成了我们定义的IChatClient接口,从而就可以在方法中直接调用接口名称来和客户端交互。

namespace signalr.Hubs
{
public class ChatHub : Hub<IChatClient>
{
public override async Task OnConnectedAsync()
{
var user = Context.ConnectionId; await Clients.Client(user).GetMyId(new ClientMessageModel { UserId = user, Context = $"回来了{DateTime.Now:yyyy-MM:dd HH:mm:ss}" });
await Clients.AllExcept(user).ReceiveMessage(new ClientMessageModel { UserId = user, Context = $"进来了{DateTime.Now:yyyy-MM:dd HH:mm:ss}" });
await base.OnConnectedAsync();
} public override async Task OnDisconnectedAsync(Exception exception)
{
var user = Context.ConnectionId; await Clients.All.ReceiveMessage(new ClientMessageModel { UserId = user, Context = $"{user}离开了{DateTime.Now:yyyy-MM:dd HH:mm:ss}" });
await base.OnDisconnectedAsync(exception);
}
}
}

我们重写了Hub的OnConnectedAsync方法,当有客户端连接进来的时候,我们给当前客户端发送一条“回来了”的内容,同时给所有在线的客户端发送一条“进来了”的通知,内容中会带上本次连接所分配给动态Guid编号。(类似与通知大家谁谁上线了)

在OnDisconnectedAsync方法中,当客户端断开连接的时候,会给所有在线客户端发送一条带有离线客户端的Guid的离开消息。(类似通知大家谁谁谁离开了)

在Startup.cs中,我们做以下设置(注入SignalR和注册Hub),同时先把在DEBUG模式下的XSRF禁用,否则访问接口会提示400错误

        public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddRazorPages()
#if DEBUG
//Debug下禁用XSRF防护,方便调试
.AddRazorPagesOptions(o =>
{
o.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
})
#endif
;
}

  

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
} app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");//注册hub
});
} 

以上服务端的基架功能就搭建好了,下面我们会来实现后台推送数据给前台Echart的功能。

在_LayoutEcharts.cshtml布局页中,我们实现引用Jquery和Echarts的JS文件,同时编写一个请求后台接口的方法,调用这个方法后,后台就会主动推送多次数据给前台。

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width" />
<script src="~/lib/echarts/dist/echarts.min.js"></script>
<script src="~/lib/jquery/dist/jquery.js"></script>
<title>@ViewBag.Title</title>
<script>
function Test() {
var chartDom = document.getElementById('main');
var myChart = window.echarts.init(chartDom);
$.ajax({
url:'/echarts',
type:'POST',
dateType: 'json',
data: { user: user},
beforeSend: function (XHR) {
console.log('I am ' + user);
myChart.showLoading({
text: '加载中。。。',
effect: 'whirling'
});
},
success:function(data) {
var option = {
series: [{
data: data.data
}]
};
myChart.setOption(option);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
},
complete:function(XHR, TS) {
myChart.hideLoading();
}
});
}
</script>
</head>
<body>
<div>
@RenderBody()
</div>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

在echarts目录的Index.cshtml中,我们实现引用Echarts组件,来渲染图表,引用SignalR来实现和服务器端数据实时交互。

@page
@model signalr.Pages.echarts.IndexModel
@{
ViewBag.Title = "Echarts图标展示(https://www.cnblogs.com/wdw984)";
Layout = "_LayoutEcharts";
} <div id="main" style="width: 800px;height:600px;"></div>
<button onclick="Test()">测试</button>
<script type="text/javascript">
var app = {}; var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option; var posList = [
'left', 'right', 'top', 'bottom',
'inside',
'insideTop', 'insideLeft', 'insideRight', 'insideBottom',
'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'
]; app.configParameters = {
rotate: {
min: -90,
max: 90
},
align: {
options: {
left: 'left',
center: 'center',
right: 'right'
}
},
verticalAlign: {
options: {
top: 'top',
middle: 'middle',
bottom: 'bottom'
}
},
position: {
options: posList.reduce(function (map, pos) {
map[pos] = pos;
return map;
}, {})
},
distance: {
min: 0,
max: 100
}
};
app.config = {
rotate: -25,
align: 'left',
verticalAlign: 'middle',
position: 'bottom',
distance: 15,
onChange: function () {
var labelOption = {
normal: {
rotate: app.config.rotate,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
position: app.config.position,
distance: app.config.distance
}
};
myChart.setOption({
series: [{
label: labelOption
}, {
label: labelOption
}, {
label: labelOption
}, {
label: labelOption
}]
});
}
};
var labelOption = {
show: true,
position: app.config.position,
distance: app.config.distance,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
rotate: app.config.rotate,
formatter: '{c} {name|{a}}',
fontSize: 16,
rich: {
name: {
}
}
}; option = {
title: {
text: '验证情况统计'
},
tooltip: {},
legend: { },
xAxis: {
data: ['数据一','数据二', '数据三','',
'数据四', '数据五','',
'数据六', '数据七', '数据八','数据九','',
'数据十','数据十一','数据十二','数据十三','数据十四'],
axisTick: {show: false},
axisLabel:{rotate: -25,interval: 0}
},
yAxis: {},
series: [{
type: 'bar',
label: {
show: true,
position: 'outside'
},
itemStyle: {
normal: {
color: function(params) {
var colorList = [
"Blue",
"Blue",
"Blue",
"",
"LightSkyBlue",
"LightSkyBlue",
"",
"Gold",
"Gold",
"Gold",
"Gold",
"",
"LightGrey",
"LightGrey",
"LightGrey",
"LightGrey",
"LightGrey"
];
return colorList[params.dataIndex];
}
}
},
data: ['0','0','0','', '0', '0', '', '0','0','0','0','', '0','0','0','0','0']
}]
}; option && myChart.setOption(option); </script>
@section Scripts
{
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/echartchat.js"></script>
}

在Index后台代码中,我们响应一个POST请求,请求中带上SignalR分配的唯一编号,后台模拟数据统计,推送给前台,这里用Task.Factory来创建一个任务执行这个操作。

        public async Task<JsonResult> OnPostAsync(string user)
{
if (string.IsNullOrWhiteSpace(user))
{
return new JsonResult(new { status = "fail", message = "NoUser" });
}
await Task.Factory.StartNew(async () =>
{
var rnd = new Random(DateTime.Now.Millisecond);
for (var i = 0; i < 10; i++)
{
await _hubContext.Clients.Client(user)
.EchartsMessage(
new[] {
$"{rnd.Next(100,300)}",
$"{rnd.Next(100,320)}" ,
$"{rnd.Next(100,310)}",
"",
$"{rnd.Next(10,30)}",
$"{rnd.Next(10,30)}",
"",
$"{rnd.Next(130,310)}",
$"{rnd.Next(130,310)}",
$"{rnd.Next(13,31)}",
$"{rnd.Next(13,31)}",
"",
$"{rnd.Next(130,310)}",
$"{rnd.Next(130,310)}",
$"{rnd.Next(13,31)}",
$"{rnd.Next(130,310)}",
$"{rnd.Next(130,310)}"}
);
await Task.Delay(2000);
}
}, TaskCreationOptions.LongRunning); return new JsonResult(new { status = "ok" });
}

随后我们访问以下这个页面,就可以看到目前这种效果

下面我们来编写前端js,用来和后端服务通过SignalR通信,在wwwroot/js下新建一个echartchat.js

"use strict";
var connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Debug)
.build();
var user = "";
var chartDom = document.getElementById('main');
var myChart = window.echarts.init(chartDom); connection.on("GetMyId", function (data) {
user = data.userId;//SignalR返回的数据字段开头是小写
console.log(user);
});
connection.on("ReceiveMessage", function (data) {
console.log(data.userId + data.context);
}); connection.on("EchartsMessage", function (data) {
console.log(data);
var option = {
series: [{
data: data
}]
};
myChart.setOption(option);//更新Echarts数据
}); connection.start().then(function () {
console.log("服务器已连接");
}).catch(function (err) {
return console.error(err.toString());
});

保存后我们再次访问页面,并点击按钮,就可以实现后台推送数据给前台echarts来展示图标的效果。

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

  1. springboot搭建一个简单的websocket的实时推送应用

    说一下实用springboot搭建一个简单的websocket 的实时推送应用 websocket是什么 WebSocket是一种在单个TCP连接上进行全双工通信的协议 我们以前用的http协议只能单 ...

  2. SignalR控制台自托管服务端向web客户端指定用户推送数据,客户端断线重连

    一.前言 SignalR是微软推出的开源实时通信框架.其内部使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式,SignalR会根据客户端和 ...

  3. Asp.net Core3.1+Vue 使用SignalR推送数据

    本文就简单使用 往前端页面推送消息 SignalR 是什么 SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Serv ...

  4. 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】

    Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...

  5. [ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  6. [ASP.NET Core 3框架揭秘] 配置[2]:读取配置数据[下篇]

    [接上篇]提到“配置”二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义 ...

  7. 006.Adding a controller to a ASP.NET Core MVC app with Visual Studio -- 【在asp.net core mvc 中添加一个控制器】

    Adding a controller to a ASP.NET Core MVC app with Visual Studio 在asp.net core mvc 中添加一个控制器 2017-2-2 ...

  8. ASP.NET Core2基于RabbitMQ对Web前端实现推送功能

    在我们很多的Web应用中会遇到需要从后端将指定的数据或消息实时推送到前端,通常的做法是前端写个脚本定时到后端获取,或者借助WebSocket技术实现前后端实时通讯.因定时刷新的方法弊端很多(已不再采用 ...

  9. SignalR实时推送

    SignalR 的实现机制与 .NET WCF 或 Remoting 是相似的,都是使用远程代理来实现.在具体使用上,有两种不同目的的接口:PersistentConnection 和 Hubs,其中 ...

随机推荐

  1. django学习-8.django模板继承(block和extends)

    1.前言 django模板继承的作用:模板可以用继承的方式来实现复用,减少冗余内容. 一般来说,一个网站里一般存在多个网页的头部和尾部内容都是一致的,我们就可以通过模板继承来实现复用. 父模板用于放置 ...

  2. C++算法代码——单词查找

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=2472 题目描述 给定 n 个长度不超过 50 的由小写英文字母组成的单词准备查询,以 ...

  3. Matplotlib 图表绘制工具学习笔记

    import numpy as np import matplotlib.pyplot as plt import pandas as pd arr1 = np.random.rand(10)#一维数 ...

  4. Java自学第8期——多线程

    1.多线程: 操作系统支持同时运行多个任务,一个任务通常是一个程序,所有运行中的程序就是一个进程().程序内部包含多个顺序执行流,每个顺序执行流就是一个线程. 并发:两个或者多个事件在同一个时间段内交 ...

  5. 上天的源码要不要——GitHub 热点速览 v.21.08

    作者:HelloGitHub-小鱼干 前几天,"机智号" 所用的飞行软件框架 F´ 被 NASA 开源了,想看 F´ 这个嵌入式的代码不妨考虑下 Sourcetrail 这个神器, ...

  6. SpringBoot2.x中的AOP机制总结(附带demo)

    寄语:刚开始学aop的时候是大三吧,老师讲的不好,我也没学好,导致现在才有个较为清晰的认知,那个时候只知道有aop, 根部不明白aop的作用,时至今日,任然觉得aop难以咀嚼,奈何平时不用面试要用,特 ...

  7. HDFS 03 - 你能说说 HDFS 的写入和读取过程吗?

    目录 1 - HDFS 文件的写入 1.1 写入过程 1.2 写入异常时的处理 1.3 写入的一致性 2 - HDFS 文件的读取 2.1 读取过程 2.2 读取异常时的处理 版权声明 1 - HDF ...

  8. HDOJ-1029(简单dp或者排序)

    Ignatius and the Princess IV hdoj-1029 这里主要是先排序,因为要找出现了一半以上的数字,所以出现的数字一定在中间 方法一: #include<iostrea ...

  9. 让人头疼的AI bug (随想)

    虽然概念上,人工智能和机器学习不等同.但是本文提及的AI,指的是基于机器学习的AI.   一个软件产品,出了错误叫bug,bug需要修.那一个机器学习的模型,准确率在那摆着呢,大伙心知肚明是有一定的犯 ...

  10. 02----python入门----基本数据类型

    关于数据分类依据 一.数字型(int) Python可以处理任意大小的正负整数,但是实际中跟我们计算机的内存有关,在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,在64位系 ...