什么是 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. flex & flex-wrap

    flex & flex-wrap https://css-tricks.com/almanac/properties/f/flex-wrap/ https://developer.mozill ...

  2. WebRTC 信令服务器

    WebRTC 信令服务器 node.js & V8 libuv socket.io https://socket.io/ node-static SSR https://github.com/ ...

  3. macOS utils

    macOS utils dr.unarchiver https://dr-unarchiver.en.softonic.com/mac https://dr-unarchiver.en.softoni ...

  4. Taro 物料市场

    Taro 物料市场 taro component demo https://taro-ext.jd.com/ https://taro-ext.jd.com/plugin/view/5caab6c68 ...

  5. API 注解 & Java API annotation

    API 注解 & Java API annotation 注解 annotation

  6. 12_MySQL如何对查询结果进行排序

    本节所涉及的sql语句: -- 排序关键字 SELECT empno,ename,hiredate FROM t_emp ORDER BY hiredate DESC; -- 排序字段相同的情况 SE ...

  7. 高性能环形队列框架 Disruptor 核心概念

    高性能环形队列框架 Disruptor Disruptor 是英国外汇交易公司LMAX开发的一款高吞吐低延迟内存队列框架,其充分考虑了底层CPU等运行模式来进行数据结构设计 (mechanical s ...

  8. SpringBoot 项目初始化

    工作之余,想要学习一下SpringBoot,通过网络大量教程最终成功运行SpringBoot项目.  第一步 首先,通过教程发现一套完整的快速搭建SpringBoot项目网站:https://star ...

  9. 解决java POI导入Excel超时问题

    由于要导入大量数据,后台会耗费很长时间,导致超时. 本项目前端request.js中设定的超时时间为150s. const service = axios.create({ baseURL: base ...

  10. SpringBoot(十):SpringBoot的简单事务管理

    SpringBoot集成Mybatis之后,进行事务管理.SpringBoot使用事务非常简单,底层依然采用的是Spring本身提供的事务. 1.在入口类中使用注解@EnableTransaction ...