什么是后台主机

在之前的 Asp.NETCore 轻松学系列中,曾经介绍过一个轻量级服务主机 IHostedService ,利用 IHostedService 可以轻松的实现一个系统级别的后台服务,该服务跟随系统启动和停止;同时,其使用异步加载和兼容注入的特性,可以很好的实现业务的扩展和隔离。

IHostedService 有一个默认的实现基类 Microsoft.Extensions.Hosting.BackgroundService,我们仅需要继承 BackgroundService 即可实现后台主机。

本文主要目的在于实现一个后台心跳广播包,所有连接到 SignalR 的客户端,通过订阅心跳包广播频道,能够自动收到服务器发送的心跳广播

广播协议定义

    public interface IHeartbeat
{
Task HeartbeatAsync(int data);
}

上面定义 了一个接口 IHeartbeat,该接口有一个异步的方法 HeartbeatAsync,主要就是心跳的定义,先不管它怎么使用,我们继续往下

定义一个泛型的 Hub

    public class WeChatHub : Hub<IHeartbeat>
{
public void Send(ChatMessage body)
{
Clients.All.RecvAsync(body);
} public override Task OnConnectedAsync()
{
Console.WriteLine("游客[{0}]进入了聊天室", this.Context.ConnectionId);
return base.OnConnectedAsync();
} public override Task OnDisconnectedAsync(Exception exception)
{
Console.WriteLine("游客[{0}]离开了聊天室", this.Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
}

上面定义了一个SignalR通信管理对象 WeChatHub ,其继承字泛型的 Hub,其中,泛型类型指定为 IHeartbeat 接口,这里的泛型即表示 SignalR 的客户端,其代表一种通信协议包,SignalR 的客户端(各种类型的)如果希望收到 IHeartbeat 定义的消息(方法实现),必须侦听 IHeartbeat 的定义(方法名称和参数)

上面的这段话比较绕口,其实我也觉得不太好理解,简单来说就是客户端要侦听一个和 SignalR 服务端定义的相同的频道,才可以收到广播。

定义后台服务主机

在定义好 SignalR 的通信协议后,接下来要做的就是实现一个后台服务主机,也就是 IHostedService 的实现。根据 Asp.Net Core 轻松学-基于微服务的后台任务调度管理器 中的提示,我们不需要实现这个接口,只需要继承 Microsoft.Extensions.Hosting.BackgroundService 即可

实现代码
    public class WeChatHubWorker : BackgroundService
{
private readonly IHubContext<WeChatHub, IHeartbeat> heartbeat;
public WeChatHubWorker(IHubContext<WeChatHub, IHeartbeat> heartbeat)
{
this.heartbeat = heartbeat;
} protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await this.heartbeat.Clients.All.HeartbeatAsync(0);
await Task.Delay(3000);
Console.WriteLine("heartbeat");
}
}
}

上面的代码比较简单,首先定义了 IHubContext<WeChatHub, IHeartbeat> 对象 heartbeat,然后在构造函数中通过注入的方式将其实例化,紧接着在 ExecuteAsync(CancellationToken stoppingToken) 方法中,执行了一个无限循环的操作,每 3000 毫秒给所有 SignalR 客户端发送一个内容为 0 的心跳包。

服务注入

实现了后台主机后,需要将其注入到 Asp.NETCore 的管道中

// 添加服务主机随主机启动
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddHostedService<WeChatHubWorker>();
...
} // 配置 SignalR 侦听的 Uri 地址
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSignalR(routes =>
{
routes.MapHub<WeChatHub>("/wechatHub");
});
...
}

通过服务注入,我们就完成了服务端的实现,下面看看 JavaScript 的实现。

JavaScript 客户端的实现

var connection = new signalR.HubConnectionBuilder()
.withUrl("/wechatHub")
.build(); connection.on("RecvAsync", function (data) {
var li = document.createElement("li");
li = $(li).text(data.userName + ":" + data.content);
$("#msgList").append(li);
}); connection.on("HeartbeatAsync", (data) => {
console.log(data);
}); connection.start()
.then(function () {
console.log("客户端已连接");
}).catch(function (err) {
console.log(err);
});

上面的代码,如果有看过前两章的同学,应该是非常熟悉的,这里就不多解释了;但是,由于本次 SignalR 服务端的 Hub 实现不太一样,所以,这里还是要解释一下。

上面的代码分别侦听了两个通道 connection.on("RecvAsync",...) 和 connection.on("HeartbeatAsync",...) ,这两个通道对应服务端的 IHeartbeat 的定义的成员名称,然后在 Callback 中的参数,也需要和 IHeartbeat 定义的一样,保证可以完整接收服务端推送的消息。

  • RecvAsync 通道用来接收和发送客户端的聊天消息(主动/被动)
  • HeartbeatAsync 通道用来接收来自服务端推送的心跳广播(被动)

运行演示

运行服务端,分别打开两个客户端,同时观察服务端和客户端的心跳输出

红圈处就是心跳广播的内容:0。

使用两个客户端分别发送聊天消息

红圈处就是聊天消息。

扩展

如果需要开通多个通道的话怎么办呢,聪明的你一定想到了,就是增加 IHeartbeat 的定义,然后在客户端订阅该频道即可。

示例代码下载

https://github.com/lianggx/Examples/tree/master/SignalR/Ron.SignalRLesson3

在后台主机中托管SignalR服务并广播心跳包的更多相关文章

  1. Azure开发者任务之七:在Azure托管服务中托管WCF服务角色

    在一个托管服务中托管一个WCF服务角色和托管一个ASP.Net Web Role基本类似. 在上一篇文章中,我们学习了如何使用WCF Service Web Role. 在本文中,我会对上一篇文章进行 ...

  2. 腾讯云 TKE Everywhere 特性发布,用户可在自有基础设施中托管 K8s 服务

    作者 孔令飞,腾讯云资深工程师,拥有大规模 Kubernetes 集群.微服务的研发和架构经验,目前专注于云原生混合云领域的基础架构开发. 朱翔,腾讯云容器服务高级产品经理,目前负责云原生混合云产品方 ...

  3. 创建自托管的SignalR服务端

    微软官方例子地址:http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host 1.说明: SignalR服务端可 ...

  4. SignalR入门之多平台SignalR服务端

    之前创建SignalR服务端是基于Web应用程序而言的.那么能不能把SignalR服务端做成控制台应用程序.Winform或windows服务呢? 答案是肯定的. 之前尽管看起来好像是IIS和ASP. ...

  5. 一、在 ASP.NET Core 中使用 SignalR

    一.介绍 SignalR 是一个用于实现实时网站的 Microsoft .NET 库.它使用多种技术来实现服务器与客户端间的双向通信,服务器可以随时将消息推送到连接的客户端. https://docs ...

  6. Signalr 实现心跳包

    项目分析: 一个实时的IM坐席系统,客户端和坐席使用IM通信,客户端使用android和ios的app,坐席使用web. web端可以保留自己的登录状态,但为防止意外情况的发生(如浏览器异常关闭,断网 ...

  7. IIS 中托管基于TCP绑定的WCF服务

    IIS 中托管基于TCP绑定的WCF服务 一.创建一个基于TCP绑定的WCF服务 1.创建一个的简单的服务具体代码如下 服务契约定义 namespace SimpleService { // 注意: ...

  8. windows 服务中托管asp.net core

    在windows 服务中托管asp.net core SDK 2.1.300 官方示例 1.添加运行标识符 xml <PropertyGroup> <TargetFramework& ...

  9. 三十、【C#.Net开发框架】WCFHosting服务主机的利用WCF服务通讯和实现思路

    回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://p ...

随机推荐

  1. 苹果公司揭秘首批列入 Swift 源代码兼容性开源项目清单

    源代码兼容性是 Swift 未来的目标.为了实现这一目标,(苹果公司的 swift 编译器团队)建立了一个源兼容性测试套件,用于根据 Swift 源代码(逐渐增加)语料库对编译器进行回归测试更改. 添 ...

  2. com.android.dex.DexException: Multiple dex files define Lcom/sina/sso/RemoteSSO;

    错误原因:ShareSDK的包里面也包含微博SDK的代码,两个Jar包含重复. 解决方法:用Winrar到ShareSDK的Jar里面把sso目录删掉,编译即可成功

  3. Flux和ExtJS MVC框架的异同点介绍

    Flux是Facebook在现有MVC框架数据流动复杂,难以设计和维护大型的前端应用的情况下设计的一种新的数据架构协议.叫做协议是因为Flux本身就像MVC一样,规定了一种机制,但是Facebook提 ...

  4. HTML 标签小细节

    简书地址:https://www.jianshu.com/p/03a23aa28a34 今天重新学习了一下HTML中标签的用法,补充并记录一下自己新学到的知识. a中的href href Contai ...

  5. 正则表达式re模块小结

    re模块的常用方法 1.compile(pattern[,flags]) 创建模式对象,一般配合其他方法使用.例如: import re #导入re模块 text = 'All that doth f ...

  6. mint-ui之picker爬坑记

    picker的数据来源为动态获取时,数据无法正常渲染!因为方法不对,所以坑大了!深刻地体会到'业不精,我之过',谨以此文,深刻地记录一下踩坑及爬坑的整个过程,以便日后不再入坑,也给后来者提供一下参考 ...

  7. 第三章之S5PV210串口初始化

    1,在start.S中执行373行b lowlevel_init跳转到/board/samsung/goni/lowlevel.S中,此代码中初始化一样硬件. 找到241行,此行执行URAT初始化,如 ...

  8. Spring Cloud Zuul 限流详解(附源码)(转)

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法. ...

  9. Netty中的EventLoop和线程模型

    一.前言 在学习了ChannelHandler和ChannelPipeline的有关细节后,接着学习Netty的EventLoop和线程模型. 二.EventLoop和线程模型 2.1. 线程模型 线 ...

  10. UIDatePicker在swift中的使用

    在上一篇文章中,创建了UISegmentedControl控件并了解它的简单用法,这篇文章主要学习DatePicker的使用,将通过Swift语言创建一个简单的例子. UIDatePicker对象:是 ...