第五章SignalR的实时高频通讯

第五章SignalR的实时高频通讯

概述:本例子演示了如果创建一个对象与其他浏览器共享实时状态的应用程序。我们要创建的应用程序为“MoveShape”,该MoveShape页面会显示一个Html Div元素,用户可以拖动,并且在用户拖动时,该元素的新位置将被发送到服务器,这样,其他所有已连接的客户端都会同步更新该元素的位置。

在这个例子中使用的应用程序是基于迪米安·爱德华兹的Demo制作的,你可以在这里看到该视频。

本例子将演示从形状的拖动事件引发时,如何发送SignalR消息开始,至每个已连接的客户端将收到该消息,并且更新本地形状的位置。

虽然使用这种方法能够很好地对SignalR的高频实时功能进行演示,但这不是一个推荐的编程模型。因为:

1)没有限制发送消息上限,会令客户端和服务器端发送和接收大量的消息,最终导致性能         下降。

2)每次接收到新位置后,形状的位置会由方法立即更新,而不是平滑地移动到新位置,所         以客户端上形状动画也会被打乱。

本例子的后面部分将演示如何创建一个定时器的功能,限制该消息在客户端和服务器端之间的发送更新消息的最大频率。本例子创建的应用程序的最终版可以在这里下载。

1.创建项目并添加SignalR和jQuery.UI NuGet包:

1)在VS2013中创建一个Asp.Net Web应用程序:

2)在新的Asp.Net项目窗口中,选择空项目并且创建:

3)在解决方案资源管理器中,右击该项目,添加一个SignalR集显器类(V2),该类命名为MoveShapeHub.cs,并将其添加到项目中,此步骤创建MoveShapeHub类,并将SignalR脚本和程序集引用添加到该项目中。

注意:您同样可以用库软件包管理器来添加SignalR引用,可以参考前面的例子。

4)使用库软件包管理器来添加jQueryUI。

在程序包管理控制台中,运行以下命令:


Install-Package jQuery.UI.Combined

该步骤将jQuery.UI库添加到项目中。

5)在解决方案资源管理器中展开脚本文件夹,您可以看到SignalR和jQuery脚本已经被添加到项目中:

2.创建基础应用程序:

在本节中,我们将创建在客户端中鼠标移动事件触发时,形状的位置发送到服务器的应用程序。至于创建服务器广播该消息给所有其他已连接的客户端的功能,我们将在后面的章节中继续讲解,现在,请把注意力集中在集线器类的创建上。

1)如果在之前您使用包控制台来添加SignalR,请先添加MoveShapeHub类到项目中。

2)使用下面的代码替代换掉的MoveShapeHub中的:


using Microsoft.AspNet.SignalR;

using Newtonsoft.Json;

namespace MoveShapeDemo

{

public class MoveShapeHub : Hub

{

public void UpdateModel(ShapeModel clientModel)

{

clientModel.LastUpdatedBy = Context.ConnectionId;

// Update the shape model within our broadcaster

Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);

}

}

public class ShapeModel

{

// We declare Left and Top as lowercase with

// JsonProperty to sync the client and server models

[JsonProperty("left")]

public double Left { get; set; }

[JsonProperty("top")]

public double Top { get; set; }

// We don‘t want the client to get the "LastUpdatedBy" property

[JsonIgnore]

public string LastUpdatedBy { get; set; }

}

}

MoveShapeHub是SignalR集线器类的一个实现。在入门教程中,我们使用了客户端直接调用的方法。在这本教程中,客户端将会发送一个包含形状的新的X及Y坐标点对象到服务器上,并且被广播给其他所有已连接的客户端。SignalR将使用JSON来序列化该对象。

我们创建了一个ShapeModel类来作为坐标属性的容器,它包含了形状位置的信息。同时,我们需要指定那些客户仅作为消息的接收端。所以服务器上的对象还包含一个成员跟踪那些客户端的数据被储存。这样,指定的客户端才不会发送它自己的形状坐标数据到服务器上。该成员使用JsonIgnore属性,防止它被序列化并被发送到客户端。

3.在应用程序启动时启用集线器:

1)  我们将把设置在应用程序启动时,自动启用集线器映射。在SignalR2.0中,这是通过增加OWIN启动类来实现的。启动类的配置方法中会调用MapSigalR方法,同时启动类会使用Assembly特性来将启动类注册到OWIN的启动处理过程中。

在解决方案资源管理器中,添加一个OWIN启动类,将其命名为Startup并添加。

2)使用一下的代码替换Startup类的内容:


using Microsoft.Owin;

using Owin;

[assembly: OwinStartup(typeof(MoveShapeDemo.Startup))]

namespace MoveShapeDemo

{

public class Startup

{

public void Configuration(IAppBuilder app)

{

app.MapSignalR();

}

}

}

4.添加客户端:

1)接下来,我们将添加客户端。添加一个Html页面,并且命名为Default.html带项目中。

2)在解决方案资源管理器中,右击刚刚添加的页面,点击设为起始页。

3)用下面的代码替换Html页面中的:


<!DOCTYPE html>

<html>

<head>

<title>SignalR MoveShape Demo</title>

<style>#shape { width: 100px;height: 100px;background-color: #FF0000;}</style>

</head>

<body>

<script src="Scripts/jquery-1.10.2.min.js"></script>

<script src="Scripts/jquery-ui-1.10.4.min.js"></script>

<script src="Scripts/jquery.signalR-2.0.0.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>

<div id="shape" />

</body>

</html>

意:请检查代码中所引用的脚本是否同脚本文件夹中的一致:

上面的Html和JS代码创建了一个红色的Div,id为Shape。在Shape拖动时,将触发它的drag事件,并将Div的位置发送给服务器。

4) 按下F5启动应用程序,复制页面的URL并打开一个新的浏览器,粘贴并打开,拖动一个浏览器的窗口中的形状,另一个浏览器的形状位置也将同步进行更新。

5.添加服务器循环:

在目前的应用中,每当服务器接收到新的消息时,都会将它们广播到所有的客户端上。同客户端的问题一样:消息总是不断发送,而不是在需要时才发送,并且可能导致连接被结果淹没。以下服务器代码就是为了解决这一问题:已实现节流出书消息的速率定时器(减少并发,防止不断并发而导致连接的实例没创建,就要发送,导致发送失败,也可以防止并发连接和并发推送消息太多,导致同一时间资源占用过多,)。

1)使用以下代码更新MoveShapeHub:


using System;

using System.Threading;

using Microsoft.AspNet.SignalR;

using Newtonsoft.Json;

namespace MoveShapeDemo

{

public class Broadcaster

{

private readonly static Lazy<Broadcaster> _instance =

new Lazy<Broadcaster>(() => new Broadcaster());

// We‘re going to broadcast to all clients a maximum of 25 times per second

private readonly TimeSpan BroadcastInterval =

TimeSpan.FromMilliseconds(40);

private readonly IHubContext _hubContext;

private Timer _broadcastLoop;

private ShapeModel _model;

private bool _modelUpdated;

public Broadcaster()

{

// Save our hub context so we can easily use it

// to send to its connected clients

_hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();

_model = new ShapeModel();

_modelUpdated = false;

// Start the broadcast loop

_broadcastLoop = new Timer(BroadcastShape, null,

BroadcastInterval, BroadcastInterval);

}

public void BroadcastShape(object state)

{

// No need to send anything if our model hasn‘t changed

if (_modelUpdated)

{

// This is how we can access the Clients property

// in a static hub method or outside of the hub entirely  _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);

_modelUpdated = false;

}


}

public void UpdateShape(ShapeModel clientModel)

{

_model = clientModel;

_modelUpdated = true;

}

public static Broadcaster Instance

{ get{ return _instance.Value; }}

}

public class MoveShapeHub : Hub

{

// Is set via the constructor on each creation

private Broadcaster _broadcaster;

public MoveShapeHub()

: this(Broadcaster.Instance){}

public MoveShapeHub(Broadcaster broadcaster)

{

_broadcaster = broadcaster;

}

public void UpdateModel(ShapeModel clientModel)

{

clientModel.LastUpdatedBy = Context.ConnectionId;

// Update the shape model within our broadcaster

_broadcaster.UpdateShape(clientModel);

}

}

public class ShapeModel

{

// We declare Left and Top as lowercase with

// JsonProperty to sync the client and server models

[JsonProperty("left")]

public double Left { get; set; }

[JsonProperty("top")]

public double Top { get; set; }

// We don‘t want the client to get the "LastUpdatedBy" property

[JsonIgnore]

public string LastUpdatedBy { get; set; }

}

}

上面的代码新增了Broadcaster类,它使用.Net框架中的Timer类来对发送消息进行节流。

由于集线器本身是暂时存在的(每次都是需要时才创建),Broadcaster被创建为一个单例。使用率延迟初始化(.Net4中新增功能),来推迟其创建时间,直到需要它为止。这是为了确保在计时器开始之前,就有集线器的实例被成功创建完毕。

调用客户端的updateShape功能,被移出集线器的updateModel方法,所有消息传入后,它将不再立即被调用。相反,需要发送至客户端的消息会已每秒25次的频率进行并发。Broadcaster类中的_broadcastLoop计时器来承担发送频率的管理功能。最终,集线器并不直接调用客户端方法,Broadcaster类需要使用GlobalHost来获得一个引用当前正在运行的操作集线器(_hubContext)。

2)按F5启动应用程序,复制窗口并拖动,将不会同上一节中的效果有太大区别。但在后台,我们已经对发送到客户端的消息进行了节流限制。

6.为客户端加入流畅的动画效果:

这个应用程序到这里,已经很完善了,但我们还能有进一步的改进。客户端的形状移动是由接受到服务器的消息而进行的。我们将使用jQueryUI库的animate功能来优化形状的移动效果,而不是直接使用服务器提供的新位置来直接改变形状的当前位置。

1)使用下面代码更新Html页面:


<!DOCTYPE html>

<html>

<head>

<title>SignalR MoveShape Demo</title>

<style>


#shape { width: 100px; height: 100px; background-color: #FF0000;}

</style>

</head>

<body>

<script src="Scripts/jquery-1.10.2.min.js"></script>

<script src="Scripts/jquery-ui-1.10.4.min.js"></script>

<script src="Scripts/jquery.signalR-2.0.0.js"></script>

<script src="/signalr/hubs"></script>

<script>

$(function () {

var moveShapeHub = $.connection.moveShapeHub,

$shape = $("#shape"),

// Send a maximum of 10 messages per second

// (mouse movements trigger a lot of messages)

messageFrequency = 10,

// Determine how often to send messages in

// time to abide by the messageFrequency

updateRate = 1000 / messageFrequency,

shapeModel = { left: 0,top: 0 },

moved = false;

moveShapeHub.client.updateShape = function (model) {

shapeModel = model;

// Gradually move the shape towards the new location (interpolate)

// The updateRate is used as the duration because by the time

// we get to the next location we want to be at the "last" location

// We also clear the animation queue so that we start a new

// animation and don‘t lag behind.

$shape.animate(shapeModel, { duration: updateRate, queue: false });

};

$.connection.hub.start().done(function () {

$shape.draggable({

drag: function () {

shapeModel = $shape.offset();

moved = true;

}

});


// Start the client side server update interval

setInterval(updateServerModel, updateRate);

});

function updateServerModel() {

// Only update server if we have a new movement

if (moved) {

moveShapeHub.server.updateModel(shapeModel);

moved = false;

}

}

});

</script>

<div id="shape" />

</body>

</html>

上面的代码将使用动画来把形状移动到新的位置上,在本例子中,我们使用100毫秒作为动画间隔。

2)按下F5启动应用程序,复制窗口并且拖动,您可以看到形状的移动比之前更流畅,形状每次移动是随着时间进行补插而不是每当有新消息传入就立即更新一次。

7.下一步:

在本例子中,您学习了如何编写客户端同服务器端高频实时通讯的SignalR应用,这种通信模式被经常用来开发网络游戏。比如:the ShootR gamecreated with SignalR

时间: 2024-07-28 23:26:18

第五章SignalR的实时高频通讯的相关文章

ASP.NET MVC4使用SignalR实现实时通讯

本文介绍在ASP.NET MVC框架中如何使用SignalR进行实时通讯 1.如何在Web中实现实时通讯 实时通讯: 例如“消息提示”.“web聊天室”等.由于web浏览器中使用的是http协议(大部分请求)进行通讯,http被称为是无状态,每次http请求和应答都是通过建立tcp连接,发送数据反馈应答,关闭tcp连接.而且必须是客户端先请求服务器端,服务器端再反馈给客户端消息.并不能实现服务器端主动给客户端推送消息的功能. 实时通讯的传统实现方法: 1.刷新整个页面.这种方法最为原始,页面中设

第三章SignalR在线聊天例子

第三章SignalR在线聊天例子 本教程展示了如何使用SignalR2.0构建一个基于浏览器的聊天室程序.你将把SignalR库添加到一个空的Asp.Net Web应用程序中,创建用于发送消息到客户端的集线器(Hubs)类,创建一个Html页面让用户在该页面上发送和接收聊天信息.对于如何在MVC5环境中创建这个聊天室程序,请参阅Getting Started with SignalR 2.0 and MVC 5. SignalR是一个开源的.Net库,用于构建需要实时用户交互或实时数据更新的We

《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth310/itt2zh上面持续更新,本文内容可能不是最新状态,请在GitHub上获得最新版本. 本文也可在http://demo.pythoner.com/itt2zh上进行格式化的预览. 第五章:异步Web服务 到目前为止,我们已经看到了许多使Tornado成为一个Web应用强有力框架的功能.它的简单性.易用性

第十五章 文件属性类的实现

                  第十五章    文件属性类的实现        根用户的权限也不能是无限大.必须考虑到保护用户的隐私!用户的文件内容.程序代码可设置为根用户也不能观看,只能是文件拥有者可以查看.修改.但根用户可以删除一切非根用户的文件.也可以查看用户的目录.所以,i_mode字符更改如下:      BU16 i_mode; // 描述文件的访问权限:文件的读.写.执行权限  // i_mode.15-13  ftype; 文件类型: 0-符号软连接文件, // 1-硬连接文

第六章SignalR的服务器广播

第六章SignalR的服务器广播 1.概述: VS可以通过 Microsoft.AspNet.SignalR.Sample NuGet包来安装一个简单的模拟股票行情应用.在本教程的第一部分,您将从头开始创建一个应用程序的简化版本.在本教程的剩余部分,您将安装NuGet包,审阅Sample中的一些附加功能. 在本模拟股票行情应用代表了实时应用中的"推" ,或称之为广播,即我们将消息通知传播给所有已连接的客户端. 首先,您将要创建该应用程序的显示表格用于显示股票数据. 接下来,服务器会随机

第四章SignalR自托管主机

第四章SignalR自托管主机 SignalR服务器通常在IIS的Asp.Net应用程序上承载,但它也可以使用自托管库来作为自托管的主机来运行(就像控制台应用程序或Windows服务那样)与Signal2.0一样,自托管库是基于.Net开放式Web接口(OWIN)来构建的.OWIN定义了.Net Web服务器和Web应用程序之间的抽象接口,将Web应用程序从服务器上解耦,使得OWIN可以在IIS之外建立自托管主机. 那我们为什么不在IIS中进行托管SignalR呢?可参考以下理由: 1)不能安装

《大道至简》第五章读后感

失败的过程也是过程,我觉得这句话很有深意,阅读完大道至简第五章,我又有了很深的感触. 首先是“做过程不是做工程”,过程是为了实现某种目的而经历的一些事情,过程有很多种,虽然经历了某种过程,但不一定能实现某种功能.做完过程的每一个阶段,并不等于做工程.做过程不是做工程的精义,也不是最终目的. 然后是“做过场”,做过场就好像是一种形式一样,做了没必要做的事情,就是浪费时间. 做工程的最终目的是实现客户的要求,工程只是一种实现的途径.最初做开发的前辈们,不用什么工程或者过程,也一样编出了程序,也一样解

大道至简第五章读后感

第五章 失败的过程也是过程 今天照样老师带领着我们阅读了大道至简第五章,阅读了<大道至简>的第五章,这章在前面的基础上又进了一步,有了技术和团队,加上有效的沟通,接下来就要接项目做工程. “虚有其表耳”,本章以<明皇实录>中的一句话来告诉我们一个深刻的道理:不要只求外表,只做形象工程,而是要透过表象,力求实质. 失败了不要紧,没有失败也就找不到自己的不足,也就不会发现自己的问题,更不用谈改进了.我们的前辈们就是在不断的失败中才总结出了“瀑布模型”“螺旋模型”等模型,方便了我们.但是

第五章 电子星球

                   第五章         电子星球   山高高兮.路长长,岁月悠悠兮.转眼空.   镇楼竹: 1. 秀竹一枝自宛然, 莫愁风雨损华年. 几番颠扑呈贞骨, 露重霜寒节更坚. 2. 纤纤凤竹长漓边, 不共山花斗野妍. 时对清流摇倩影, 溪流常伴乐怡然. 3. 坚节何愁风雨多, 晴天朗日更婆娑. 生凉不荫趋炎客, 惹得骚人为咏歌.   咏经典物理.戏现代理论物理: 在山泉水洁如冰, 溅玉飞珠迸有声. 流入大江清浊混, 滔滔何日见澄明.     一.   批驳现代理论