SignalR 简介





4.00/5 (7投票s)
SignalR 与 ASP.NET 的使用入门
引言
本技巧介绍了使用 SignalR 进行实时通信的入门信息,适用于 Web 应用程序,服务器可以立即将信息推送给客户端,而无需等待客户端从服务器请求数据。
SignalR 在聊天应用程序、仪表板或 游戏(就万维网而言)中非常有用,但对于工业桌面应用程序也至关重要。
如果您正在使用 AJAX 来使您的 Web 应用程序更具响应性,那么 SignalR 是一个有用的替代方案。
您可以通过启用日志记录来确定 SignalR 使用的传输类型
$.connection.hub.logging = true;
如果您然后在 Web 浏览器上打开调试工具,您将看到一行类似于“SignalR:Websocket 已打开”。
什么是 SignalR?
通常,如果要从服务器获取客户端上已更改的更多数据,则必须使用 HTTP GET
执行轮询,这就是 AJAX 的工作方式。 如果您想要接近实时的任何内容,那么您必须非常频繁地进行轮询。 相反,使用 SignalR,您可以拥有一个持久连接,客户端可以调用服务器,服务器也可以调用客户端。
因此,SignalR 是一个为开发人员简化实时通信的库,其中服务器和客户端连接是持久的,这与传统的 HTTP 范例不同。
如果您的计算机使用 Windows 8 或 Windows Server 2012 或更高版本,以及 .NET Framework 4.5,那么 SignalR 将使用 WebSocket; 否则,它将使用 HTML 5 传输或 Comet 传输。
连接和 Hubs
客户端和服务器之间通信的两种模型是持久连接和 Hubs。
连接代表用于发送单收件人、分组或广播消息的端点。
Hub 是持久连接之上的一层,允许您的客户端和服务器直接相互调用方法。 这种模型对于使用过 .NET Remoting 的 .NET 开发人员来说会很熟悉。 这允许您传递强类型参数。
当通过通信通道发送完整对象时,它们会使用 JSON 进行序列化。
您可以使用 Fiddler 等工具监控方法调用。
创建应用程序
首先创建一个空的 ASP.NET Web 项目,右键单击以添加 SignalR,确保您的项目以 .NET 4.5 或更高版本为目标
选择 SignalR
或者您只需在包管理器控制台中键入此内容
PM> install-package Microsoft.AspNet.SignalR
PM> install-Package jQuery.UI.Combined
PM> install-Package Microsoft.Web.Infrastructure
接下来,向您的应用程序添加一个名为 Startup
的类,该类在应用程序启动时映射您的 SignalR
hub
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(CodeProjectSignalRSample.Startup))]
namespace CodeProjectSignalRSample
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR();
}
}
}
如果您还没有,那么添加以下 SignalR
hub 类
using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
namespace CodeProjectSignalRSample
{
public class MyHub : 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; }
}
}
客户端可以直接调用 UpdateModel
方法,然后该方法会广播到所有连接的客户端。 它会自动使用 JSON 进行序列化。
现在通过向您的解决方案添加一个 HTML 页面来添加客户端
<!DOCTYPE html>
<html>
<head>
<title>SignalR 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.1.0.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function () {
var moveShapeHub = $.connection.myHub,
$shape = $("#shape"),
shapeModel = {
left: 0,
top: 0
};
MyHub.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();
MyHub.server.updateModel(shapeModel);
}
});
});
});
</script>
<div id="shape" />
</body>
</html>
另外,请确保您的 web.config 文件包含以下内容
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration>
启动应用程序,然后将 URL 复制到浏览器的新实例中。 尝试四处移动形状。
添加客户端循环
每次鼠标移动都发送位置会产生大量的网络流量,因此您可以通过将此代码放入 HTML 文件中来限制它
<!DOCTYPE html>
<html>
<head>
<title>SignalR Demo</title>
<style>
#shape {
width: 100px;
height: 100px;
background-color: #FF0000;
}
</style>
</head>
<body>
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery-ui-1.11.3.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.0.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function () {
var moveShapeHub = $.connection.myHub,
$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;
$shape.css({ left: model.left, top: model.top });
};
$.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>
添加服务器循环
将以下代码添加到您的 hub 也可以减少服务器端的流量
using System;
using System.Threading;
using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
namespace CodeProjectSignalRSample
{
public class Broadcaster
{
private readonly static Lazy
_instance =
new Lazy
(() => 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<myhub>();
_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 MyHub : Hub
{
private Broadcaster _broadcaster;
public MyHub()
: this(Broadcaster.Instance)
{
}
public MyHub(Broadcaster broadcaster)
{
_broadcaster = broadcaster;
}
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; }
}
}
向客户端添加平滑动画
添加以下代码以使形状在 1000 毫秒内从旧位置移动到新位置
<!DOCTYPE html>
<html>
<head>
<title>SignalR Demo</title>
<style>
#shape {
width: 100px;
height: 100px;
background-color: #FF0000;
}
</style>
</head>
<body>
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery-ui-1.11.3.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.0.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function () {
var moveShapeHub = $.connection.myHub,
$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;
$shape.css({ left: model.left, top: model.top });
};
$.connection.hub.start().done(function () {
$shape.draggable({
drag: function () {
shapeModel = $shape.offset();
moved = true;
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 });
}
});
// 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>