目录

一、前言

微软官方给的说明:ASP.NET SignalR是ASP.NET开发人员的新库,可以轻松开发实时Web功能。SignalR允许服务器和客户端之间的双向通信。服务器现在可以在连接的客户端可用时立即将其推送到连接的客 SignalR支持Web套接字,并且可以回退到旧版浏览器的其他兼容技术。SignalR包括用于连接管理的API(例如,连接和断开事件),分组连接和授权。来源:https://www.asp.net/signalr;可以从这里找到SignalR的简介、教程等,以及在最新的平台 .NET Core 2.1 中使用。

下面就说说自己在实际操作中遇到的一些问题或者所悟。

首先说一下当时为什么选择SignalR。

  1. 需求:项目中需要知道每个人的实时待办数量的数据;
  2. 原先的解决方案:后台定时去计算这些待办数量存库,前端定时的请求接口进行查询,这样有一些问题,首先效率的问题,还有就是实时性问题,后面考虑改变方案;
  3. 新的解决方案:在案件有变动的时候,触发计算该案件影响到的人员的代办数量,并实时发送到客户端,这里实时发送用到的就是SignalR;

已经确定了方案,那就是按照这个方案的思路来具体的实现。细分开来里面有这两个大的部分:1、服务端;2、客户端。具体的实现中还要注意一些细节方面的东西,下面就分别说下实现。

二、服务端

服务类型:发布站点,宿主服务、客户端

2.1、站点服务端

添加 Startup 类,对应代码(注册服务,启动 SignalR 服务)

public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
app.UseCors(CorsOptions.AllowAll); var hubConfig = new HubConfiguration() { EnableJavaScriptProxies = false };
app.MapSignalR(hubConfig); // 配置集群底板数据库
RedisConfiguration redisConfig = RedisSectionHandler.GetConfig();
GlobalHost.DependencyResolver.UseRedis(redisConfig.RedisHosts[].Host, redisConfig.RedisHosts[].Port, redisConfig.Password, "signalR_loginHub");
}
}

2.2、宿主服务或客户端

对应的服务代码,加载、组装和启动 Web 应用程序

public class SignalRServer
{
private IDisposable app;
private static string domain = "http://localhost:10110"; static SignalRServer()
{
domain = ConfigurationManager.AppSettings["Domain"] ?? domain;
Console.WriteLine("获取配置:" + domain);
} public void StartSignalR()
{
Console.WriteLine("消息服务运行在:" + domain); app = WebApp.Start(domain, builder =>
{
//// 该值表示连接在超时之前保持打开状态的时间长度。
////默认为110秒
//GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110); ////该值表示在连接停止之后引发断开连接事件之前要等待的时间长度。
////默认为30秒
//GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30); ////用于表示两次发送保持活动消息之间的时间。如果启用,此值必须至少为两秒。设置为 null 可禁用。
////默认为10秒,设置DisconnectTimeout后默认为DisconnectTimeout的3分之一
//GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10); builder.UseCors(CorsOptions.AllowAll);
builder.MapSignalR(new HubConfiguration
{
EnableJSONP = true,
EnableDetailedErrors = true,
EnableJavaScriptProxies = true
});
});
} public void StopSignalR()
{
if (app != null)
{
app.Dispose();
}
}
}

2.3、持久连接和集线器

signalR内部有两类对象:

  1. Http持久连接(Persisten Connection)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
  2. Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

这里贴下在项目中用到的集线器的用法

public class MyHub : Hub
{public override Task OnConnected()
{
LogHelper.WriteInfo($"用户ConnectionId是:{Context.ConnectionId},连接到服务器。"); string userId = Context.QueryString["userId"];
// 业务代码处理return base.OnConnected();
} /// <summary>
/// 超时或者断开连接时调用
/// </summary>
/// <param name="stopCalled">客户端正常关闭:true,超时连接:false</param>
/// <returns></returns>
public override Task OnDisconnected(bool stopCalled)
{
LogHelper.WriteInfo($"用户ConnectionId是:{Context.ConnectionId},断开连接。"); // 业务代码处理return base.OnDisconnected(stopCalled);
} /// <summary>
/// 服务器端发送消息的方法
/// </summary>
/// <param name="msg"></param>
public void Send(string msg)
{
// 调用所有客户注册的本地JS方法
Clients.All.receive(msg);
}
} /// <summary>
/// 服务端主动向浏览器发送消息(向单独用户发送消息)
/// </summary>
public class SignalrServerToClient
{
static readonly IHubContext _myHubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); public static void SendAll(string connectionId, string message)
{
_myHubContext.Clients.All.receive(message);
} public static void SendToUser(string connectionId, string message)
{
_myHubContext.Clients.Client(connectionId).receive(message);
}
}

三、客户端

3.1、使用代理客户端

在使用 Hub类的实时,客户端的类名第一个字母要小写:MyHub =》myHub

使用代理的代码如下:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>SignalR Test</title>
<script src="Scripts/jquery-3.3.1.min.js"></script>
<script src="Scripts/jquery.signalR-2.3.0.min.js"></script>
<script src="http://localhost:10110/signalr/hubs"></script> // 要添加这个引用才能使用代理
<link href="CSS/Index.css" rel="stylesheet" />
</head>
<body>
<div class="box">
<ul id="list"></ul>
<div id="userBox" class="sendBox">
<div>
<textarea id="user"></textarea>
<button id="btn_senduser">发送用户信息</button>
</div>
</div>
<div id="msgBox" class="sendBox" style="display:none">
<div>
<textarea id="msg"></textarea>
<button id="btn_send">发送</button>
</div>
</div>
</div>
</body>
</html>
<script type="text/javascript">
$(function () {
$.connection.hub.url = "http://localhost:10110/signalr";
$.connection.hub.qs = { "userId": 66 }; var chat = $.connection.myHub; chat.client.receive = function (msg) {
var $list = $("#list");
if ($list.find("li").length <= 0) {
$list.append("<li>" + msg + "</li>");
} else {
$("<li>" + msg + "</li>").insertBefore("#list li:first");
}
}; $.connection.hub.start().done(function (data) {
$("#list").append("<li>连接情况:" + data + "我的ID是:" + data.id + "</li>"); $("#btn_senduser").bind("click", function () {
chat.server.receive($("#user").val());
$("#user").val("");
$("#userBox").hide();
$("#msgBox").show();
}); $("#btn_send").bind("click", function () {
chat.server.send($("#msg").val());
$("#msg").val("");
}); $("#msg").bind("keyup", function (e) {
if (e.keyCode === 13) {
chat.server.send($("#msg").val());
$("#msg").val("");
}
}); }).fail(function () {
alert("实时消息服务连接失败!");
}); });
</script>

3.2、不使用代理客户端

不使用代理的客户端代码:

<html>

<head>
<script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
<script src="Scripts/jquery.signalR-2.3.0.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
var loginhub = null; $("#btnLogin").click(function () {
if ($("#txtUserId").val() == "") {
alert("请填写 userid");
return;
} var conn = $.hubConnection("http://192.168.199.71:10200/signalr");
conn.qs = {"userid": $("#txtUserId").val()};
loginhub = conn.createHubProxy('loginHub');
loginhub.on("beLogout", function (message) {
$("#msg").html(message);
}); conn.start().done(function (data) {
$("#msg").html("登陆成功,connectionid:" + data.id);
});
}); $("#btnLogout").click(function () {
if (loginhub == null) {
alert("没有登陆,不能进行退出操作");
return;
}
loginhub.invoke("logout", $("#txtUserId").val()).done(function () {
$("#msg").html("退出成功");
});
}); });
</script>
</head> <body>
userid:<input id="txtUserId" /><span id="msg"></span>
<br />
<button id="btnLogin">登陆</button><br />
<button id="btnLogout">退出</button><br />
</body> </html>

这两者的区别:

1、使用代理要在头部添加这样的引用—— <script src="http://localhost:10110/signalr/hubs"></script> ;

2、使用代理可以直接点出方法,不使用代理要自己手动绑定方法;

SignalR 初体验的更多相关文章

  1. SignalR初体验

    简介 ASP .NET SignalR[1]  是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以 ...

  2. .Net Core SignalR 初体验

    前言 Asp.Net SignalR已经出来很久了,但是一直没有静下心来好好看看.昨天花了几个小时的时间看了下.首先借鉴了官方文档,如何搭建一个SignalR的Demo. 参考文章:https://d ...

  3. .NET WebSockets 核心原理初体验

    上个月我写了<.NET gRPC核心功能初体验>, 里面使用gRPC双向流做了一个打乒乓球的Demo, 实时双向这两个标签是不是很熟悉,对, WebSockets也可以做实时双向通信. 本 ...

  4. .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验

    不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...

  5. Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验

    Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...

  6. Spring之初体验

                                     Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...

  7. Xamarin.iOS开发初体验

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0

  8. 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...

  9. 【Knockout.js 学习体验之旅】(1)ko初体验

    前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...

随机推荐

  1. 在 VMware 中安装 MacOS 全记录

    在 VMware 15 中安装 MacOS Mojave 安装文件 下载:Unlocker v3.0 for VMware 15地址:https://github.com/DrDonk/unlocke ...

  2. 批处理cmd开启,关闭防火墙

    管理员启动dos窗口 开启防火墙: netsh advfirewall set allprofiles state on 关闭防火墙: netsh advfirewall set allprofile ...

  3. vector 使用pair对

    pair是一种序偶结构<x,y> 如果我们希望使用pair但又不需要map对其排序,可以在vector中使用pair对 插入pair对使用make_pair<typename,typ ...

  4. Linux基础-03-用户、群组

    1. 用户及passwd文件 1) /etc/passwd文件的功能:存储所有用户的相关信息,该文件也被称为用户信息数据库(Database). 2) /etc/passwd文件每个字段的具体含义: ...

  5. Springboot对JPA的支持及使用

    目的: 1.springboot之jpa支持 2.Springboot+bootstrap界面版之增删改查及图片上传 springboot之jpa支持 导入相关pom依赖 <dependency ...

  6. fatfs系统的移植

    integer.h   FATFS的数据类型定义(一般不需要更改,其他的文件都需要引用这个文件的内容) ffcon.h FATFS的配置文件,配置项的各个参数都需要在这里修改 一个细致的讲解fatfs ...

  7. vue 写一个炫酷的轮播图

    效果如上图: 原理: 1.利用css 的 transform 和一些其他的属性,先选五张将图片位置拍列好,剩余的隐藏 2.利用 js 动态切换类名,达到切换效果 css代码如下 .swiper-cer ...

  8. 集合并卷积的三种求法(分治乘法,快速莫比乌斯变换(FMT),快速沃尔什变换(FWT))

    也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级 ...

  9. ASP.net Web API综合示例

    目录 概述 功能介绍 程序结构 服务器端介绍 客户端介绍 “契约” Web API设计规则 并行写入冲突与时间戳 身份验证详解 Web API验证规则 客户端MVVM简介 Web.Config 本DE ...

  10. SQL Server存储过程中字符串前加N的含义

    使用方法: N'字符串' 解释: 意思为后面的数据类型为NChar或者NVarchar 使用N前缀 在服务器上执行的代码中(例如在存储过程和触发器中)显示的Unicode字符串常量必须以大写字母N为前 ...