教程简介

SignalR的好处是可以让多个客户端之间进行互动,比如这篇教程就展示了当你在页面上拖动矩形方块的同时,其它打开这个页面的用户也将会看到你拖动的轨迹以及最终的结果,当然他们也可以通过拖动该方块来改变你的网页上该方块的位置。如果对其进行扩展一下,说不定还能搞出来一个供多人完成的涂鸦墙。该教程出自High-Frequency Realtime with SignalR 2,有兴趣的可以看原教程。

创建项目

1.打开Visual Studio,并创建一个新的ASP.NET Web Application项目,选择Empty模板,然后点击OK,创建项目。

2.依次单击Tools | NuGet Package Manager | Package Manager Console,打开NuGet控制台。

3.输入命令Install-Package Microsoft.AspNet.SignalR安装SignalR组件。

4.输入命令Install-Package jQuery.UI.Combined安装jQuery UI,并升级jQuery文件。

5.打开Solution Explorer可以看到对应的jQueryjQueryUISignalR,如下图所示。

添加Hub

1.右击项目文件,依次点击Add | New Item | Web | SignalR | SignalR Hub Class(v2),然后创建一个名为MoveShapeHub.cs的Hub文件。

2.在MoveShapeHub类中输入下面这段代码。

using Newtonsoft.Json;
using Microsoft.AspNet.SignalR; namespace MoveShapeDemo
{
public class MoveShapeHub : Hub
{
public void UpdateModel(ShapeModel clientModel)
{
clientModel.LastUpdateBy = Context.ConnectionId; // Clients.Others.updateShape(clientModel);
Clients.AllExcept(clientModel.LastUpdateBy).updateShape(clientModel);
}
} public class ShapeModel
{
/// <summary>
/// 使用JsonProperty指定的名称来序列化该属性
/// </summary>
[JsonProperty("left")]
public double Left { get; set; } /// <summary>
/// 同上
/// </summary>
[JsonProperty("top")]
public double Top { get; set; } /// <summary>
/// 使用JsonIgnore表示序列化时忽略该属性
/// </summary>
[JsonIgnore]
public string LastUpdateBy { get; set; }
}
}

添加Hub的启动文件

1.使用SignalR必需要添加启动文件(而这个启动文件则是基于OWIN的),在Solution Explorer里,右键项目,然后依次点击Add | New Item | Web | General | OWIN Startup Class,创建一个名为Startup.cs的启动文件。

2.在Startup类中输入下面这段代码。

using Microsoft.Owin;
using Owin; [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))] namespace MoveShapeDemo
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}

添加客户端文件

1.服务端的工作完成后,再来编写客户端的代码,右键项目,并在根目录新建一个名为index.html的网页文件。

2.将下面这段代码拷备到新建的网页文件中。

<!DOCTYPE html>
<html>
<head>
<title>SignalR MoveShape Demo</title>
<style>
#shape {
width: 100px;
height: 100px;
background-color: #FF0000;
}
</style>
<script src="Scripts/jquery-1.12.4.js"></script>
<script src="Scripts/jquery-ui-1.12.1.js"></script>
<script src="Scripts/jquery.signalR-2.2.1.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function () {
var moveShapeHub = $.connection.moveShapeHub,
$shape = $("#shape"),
shapeModel = {
left: 0,
top: 0
}; // 声明的回调方法
moveShapeHub.client.updateShape = function (model) {
shapeModel = model;
$shape.css({ left: model.left, top: model.top });
}; // 建立连接,并调用服务器方法
$.connection.hub.start().done(function () {
$shape.draggable({
drag: function () {
shapeModel = $shape.offset();
moveShapeHub.server.updateModel(shapeModel);
}
});
});
});
</script>
</head>
<body>
<div id="shape" />
</body>
</html>

3.准备注绪后,按F5,并使用多个浏览器打开,试着在任何一个浏览器页面上拖动这个方块,就可以看到不管你在哪一个浏览器页面托动这个方块,所有打开的页面上这个方块都会跟着移动。不过这个测试案例并不是很好,因为只要你移动方块,它就会与服务器进行交互,这样就会造成网络带宽不必要的浪费,所以要改进一下。

改进一:客户端定时发送移动的新坐标

为了不使客户端移动的每一像素都发送给服务器,我们决定采用setInterval函数来让客户端定时把最新的坐标信息发送给服务器,并且加上一个开关,只有在移动后才会发送最新的坐标数据。

将静态页的代码改为下面这个样子(只给出脚本部分)

<script>
var moveShapeHub,
shapeModel,
moved; $(function () {
moveShapeHub = $.connection.moveShapeHub;
moved = false;
shapeModel = {
left: 0,
top: 0
}; var $shape = $("#shape"),
messageFrequency = 10, // 限定每秒最多发送10个消息
updateRate = 1000 / messageFrequency; // 设置每个消息发送的间隔时间 // 声明的回调方法
moveShapeHub.client.updateShape = function (model) {
shapeModel = model;
$shape.css({ left: model.left, top: model.top });
}; // 建立连接,并调用服务器方法
$.connection.hub.start().done(function () {
$shape.draggable({
drag: function () {
shapeModel = $shape.offset();
moved = true;
}
}); // 使用定时器定时向服务器发送新坐标
setInterval(updateServerModel, updateRate);
});
}); function updateServerModel() {
if (moved) {
moveShapeHub.server.updateModel(shapeModel);
moved = false;
}
}
</script>

重新运行程序,再次拖动矩形时,客户端就会以每秒最多10次的频率来与服务器传递数据,虽然节省了带宽,不过其它客户端的矩形在被移动时就会像PPT一样,没有那么顺滑了,不过这都不叫事儿。

改进二:服务器定时将数据广播给其它客户端

在这个例子中,服务器端只要一接收到数据就将它广播给其它的客户端,而这就会存在与上面客户端类似的问题,所以我们这次也要对服务器端的代码进行改造,让它定时向客户端广播数据。

MoveShapeHub.cs里的代码改为下面的代码。

using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
using System;
using System.Threading; namespace MoveShapeDemo
{
public class Broadcaster
{
/// <summary>
/// 延时加载的功能
/// </summary>
private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster());
private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40);
private readonly IHubContext _hubContext;
private Timer _broadcastLoop;
private ShapeModel _model;
private bool _modelUpdated; private Broadcaster()
{
// 获得对指定Hub连接的引用
_hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
_model = new ShapeModel();
_modelUpdated = false;
_broadcastLoop = new Timer(BroadcastShape, null, BroadcastInterval, BroadcastInterval);
} public static Broadcaster Instance
{
get { return _instance.Value; }
} /// <summary>
/// 将最新坐标广播给其它客户端(被定时器循环调用)
/// </summary>
/// <param name="state"></param>
private void BroadcastShape(object state)
{
if (_modelUpdated)
{
_hubContext.Clients.AllExcept(_model.LastUpdateBy).updateShape(_model);
_modelUpdated = false;
}
} public void UpdateShape(ShapeModel clientModel)
{
_model = clientModel;
_modelUpdated = true;
}
} /// <summary>
/// Hub的生命周期比较短(只有在需要的时候才会创建)
/// </summary>
public class MoveShapeHub : Hub
{
private Broadcaster _broadcaster; public MoveShapeHub() :
this(Broadcaster.Instance)
{ } public MoveShapeHub(Broadcaster broadcaster)
{
_broadcaster = broadcaster;
} public void UpdateModel(ShapeModel clientModel)
{
clientModel.LastUpdateBy = Context.ConnectionId; // 将原来的广播消息给其它客户端的代码交由Broadcaster来管理,
// 这样就能够避免只要有数据传入就立即广播给其它客户端的情况
// 出现。取而代之的是使用Timer类的定时广播功能,从而达到从
// 服务器这端节省资源的目的。
_broadcaster.UpdateShape(clientModel);
}
} public class ShapeModel
{
/// <summary>
/// 使用JsonProperty指定的名称来序列化该属性
/// </summary>
[JsonProperty("left")]
public double Left { get; set; } /// <summary>
/// 同上
/// </summary>
[JsonProperty("top")]
public double Top { get; set; } /// <summary>
/// 使用JsonIgnore表示序列化时忽略该属性
/// </summary>
[JsonIgnore]
public string LastUpdateBy { get; set; }
}

重新运行项目,其实运行效果和之前并没有什么两样,只是服务器端不再会接到客户端发来的数据后就立即广播给其它客户端,而是会做记录,等到计器器下一次起动广播时再把这个坐标广播给其它客户端,不过被广播的用户依然会有看PPT的感觉。

改进三:为客户端添加动画效果

现在我们还要做最后一点改动,那就是解决被移动的矩形看起来像PPT一样的问题,只里要将客户端声明的回调方法(updateShape)做适当的改动即可,代码如下所示。

// 声明的回调方法
moveShapeHub.client.updateShape = function (model) {
shapeModel = model; // 使用动画效果使得矩形被移动时看起来更平滑,
// duration:动画的持续时间
// queue:是否在效果队列中放置动画
$shape.animate(shapeModel, { duration: updateRate, queue: false });
};

使用SignalR实现即时通讯功能的更多相关文章

  1. MVC中使用SignalR打造酷炫实用的即时通讯功能附源码

    前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯.由于当时走的太急,忘记把代码拿出来.想想这已经是大半年前的事情了,时间过 ...

  2. MVC中使用SignalR打造酷炫实用的即时通讯功能(轉載)

    資料來源:http://www.fangsi.net/1144.html 前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯 ...

  3. [置顶] Xamarin android中使用signalr实现即时通讯

    前面几天也写了一些signalr的例子,不过都是在Web端,今天我就来实践一下如何在xamarin android中使用signalr,刚好工作中也用到了这个,也算是总结一下学到的东西吧,希望能帮助你 ...

  4. Android BLE与终端通信(四)——实现服务器与客户端即时通讯功能

    Android BLE与终端通信(四)--实现服务器与客户端即时通讯功能 前面几篇一直在讲一些基础,其实说实话,蓝牙主要为多的还是一些概念性的东西,当你把概念都熟悉了之后,你会很简单的就可以实现一些逻 ...

  5. Android WebSocket实现即时通讯功能

    最近做这个功能,分享一下.即时通讯(Instant Messaging)最重要的毫无疑问就是即时,不能有明显的延迟,要实现IM的功能其实并不难,目前有很多第三方,比如极光的JMessage,都比较容易 ...

  6. 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  7. android环境下的即时通讯

    首先了解一下即时通信的概念.通过消息通道 传输消息对象,一个账号发往另外一账号,只要账号在线,可以即时获取到消息,这就是最简单的即使通讯.消息通道可由TCP/IP UDP实现.通俗讲就是把一个人要发送 ...

  8. 用smack+openfire做即时通讯

    首发:个人博客 必须说明:smack最新的4.1.1,相对之前版本变化很大,而且资料缺乏,官方文档也不好,所以还是用老版本3.2.2吧.这篇博文中的代码是4.1.1版的,但不推荐用它.用openfir ...

  9. openfire+spark+smack实现即时通讯

    近公司项目需要用到即时通讯功能,经过调研发现openfire+spark+smack可以实现.在网上找了很久,资料都十分有限,即使有些朋友实现了也说的不清不楚.于是决定自己研究,耗时一周的时间实现了文 ...

随机推荐

  1. Reactive Extensions(Rx) 学习

    Bruce Eckel(著有多部编程书籍)和Jonas Boner(Akka的缔造者和Typesafe的CTO)发表了“反应性宣言”,在其中尝试着定义什么是反应性应用. 这样的应用应该能够: 对事件做 ...

  2. [nRF51822] 8、基础实验代码解析大全 · 实验11 - PPI

    前一篇分析了前十个基础实验的代码,从这里开始分析后十个~ 一.PPI原理: PPI(Programmable Peripheral Interconnect),中文翻译为可编程外设互连. 在nRF51 ...

  3. 解决vue与传统jquery插件冲突

    比如基于jquery的select2插件,在vue下单独用有很多问题,其实对于这类插件,可以用vue的自定义指令和组件来包装,解决冲突的问题.引用官方vue1.0和2.0的两个例子,学习一下. 例子1 ...

  4. Android开发学习之路-该怎么学Android(Service和Activity通信为例)

    在大部分地方,比如书本或者学校和培训机构,教学Android的方式都基本类似,就是告诉先上原理方法,然后对着代码讲一下. 但是,这往往不是一个很好的方法,为什么? ① 学生要掌握这个方法的用途,只能通 ...

  5. DLL导出函数和类的定义区别 __declspec(dllexport)

    DLL导出函数和类的定义区别 __declspec(dllexport) 是有区别的, 请看 : //定义头文件的使用方,是导出还是导入 #if defined(_DLL_API) #ifndef D ...

  6. iOS--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook等系统服务开发汇总

    iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...

  7. is_null, empty, isset, unset对比

    is_null, empty, isset, unset 我们先来看看这4个函数的描述 isset 判断变量是否已存在(配置)unset 把变量删除(释放)掉empty 判断变量是否为空is_null ...

  8. weblogic配置数据源

    启动weblogic 管理服务器,使用管理用户登录weblogic管理控制台   打开管理控制台后,在左侧的树形域结构中,选择服务->数据源. 在右侧的窗口中,选择 新建->一般数据源   ...

  9. 将数据从MySQL迁移到Oracle的注意事项

    将数据从MySQL迁移到Oracle的注意事项1.自动增长的数据类型处理MYSQL有自动增长的数据类型,插入记录时不用操作此字段,会自动获得数据值.ORACLE没有自动增长的数据类型,需要建立一个自动 ...

  10. 谈谈service层在mvc框架中的意义和职责

    mvc框架由model,view,controller组成,执行流程一般是:在controller访问model获取数据,通过view渲染页面. mvc模式是web开发中的基础模式,采用的是分层设计, ...