目录

一、前言

微软官方给的说明: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. Spring的定时任务@Scheduled(cron = "0 0 1 * * *")

    指定某个方法在特定时间执行,如: cron="0 0 1 1 * ?" 即这个方法每月1号凌晨1点执行一次 关于这个注解的解释网上一大堆 但是今天遇到个问题,明明加了注解@Sche ...

  2. mac 上更改环境变量

    第一次配置Mac的环境变量,到网上转了一圈才找到正确方法. 打开终端,新建.bash_profile文件在~/目录下(如果电脑里已经有了这个文件,跳过这一步) touch ~/.bash_profil ...

  3. [转帖]oracle补丁类型

    oracle补丁类型 https://www.cnblogs.com/liang545621/p/9417919.html 介绍挺好的 跟现在的也比较类似呢.   名称 说明 Release ¤ 标准 ...

  4. [转帖]Apache Kylin 概述

    Apache Kylin 概述 https://www.cnblogs.com/xiaodf/p/11671095.html 1 Kylin是什么 今天,随着移动互联网.物联网.AI等技术的快速兴起, ...

  5. Mybatis之关联关系(一对多、多对多)

    目的: Mybatis关系映射之一对多 Mybatis关系映射之多对多 Mybatis关系映射之一对多 一对多 (订单对应多个订单项) 多对一  (订单项对应一个订单) 其是映射关系的基层思维是一样的 ...

  6. RMAN备份,catalog注册rman带库备份信息

    客户需求:测试恢复的过程中,控制文件是全备时期的,recover database无法恢复到指定日期,控制文件中缺失后续新的归档备份信息. 方法:1.控制文件rman注册后续带库中的归档备份: 2.使 ...

  7. C# Aforge设置摄像头视频属性和控制属性

    修改后的代码:github 一.调用windows自身摄像头属性设置窗口 使用VideoCaptureDevice对象的DisplayPropertyPage(IntPtr parentWindow) ...

  8. linux 系统运维工具13款

    1. 查看进程占用带宽情况 - Nethogs Nethogs 是一个终端下的网络流量监控工具可以直观的显示每个进程占用的带宽. 下载:http://sourceforge.net/projects/ ...

  9. 3_PHP表达式_5_数据类型转换_类型强制转换

    以下为学习孔祥盛主编的<PHP编程基础与实例教程>(第二版)所做的笔记. PHP类型转换分为类型自动转换和类型强制转换. 3.5.2 类型强制转换 类型强制转换允许编程人员手动将变量的数据 ...

  10. MySQL性能测试调优

    MySQL性能测试调优 操作系统 基本操作 查看磁盘分区mount选项 $ mount 永久修改分区mount选项(系统重启后生效) 修改文件 /etc/fstab 中对应分区的mount optio ...