创建一个空的 Web 项目,并在 Nuget 里面添加 SignalR,jQuery UI 包,添加以后项目里包含了 jQuery,jQuery.UI ,和 SignalR 的脚本。
添加一个 SignalR Hub 类,并命名为 MoveShapeHub ,更新代码。
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace SignalRDemo3 { 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; } } }
当程序启动的时候启动Hub
添加 Owin 类,并在里面配置 SignalR
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(SignalRDemo3.Startup))] namespace SignalRDemo3 { 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.MapSignalR(); } } }
添加一个名为 Index 的 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-3.1.1.min.js"></script> <script src="Scripts/jquery-ui-1.12.1.min.js"></script> <script src="Scripts/jquery.signalR-2.2.2.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 和 JavaScript 代码创建一个名为 Shape 的 Div ,并且通过jQuery库给 Shape 提供了拖拽功能,并通过拖拽事件向服务器发送 Shape 的当前位置。
现在可以 F5 启动调试看效果,当程序启动以后,打开另一个浏览器窗口,输入地址,你可以在一个窗口拖拽红色的 Shape,另一个窗口的 Shape 也会跟着动。
如果每次鼠标移动都发送数据到服务端,那就需要很多网络流量,我们必须降低发送数据的频率。我们可以通过 setInterval 函数,设置一个固定的时间来发送数据到服务器。
<!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-3.1.1.min.js"></script> <script src="Scripts/jquery-ui-1.12.1.min.js"></script> <script src="Scripts/jquery.signalR-2.2.2.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 2 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 2, // 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>
可以用上面的代码更新刚才的 Index.html页面,然后F5调试,可以发现现在拖动一个 Shape 以后在另一个浏览器里的 Shape 半秒钟才会更新。
更新 MoveShapeHub.cs
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; using System; using System.Threading; namespace SignalRDemo3 { public class Broadcaster { private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster()); // We're going to broadcast to all clients once 2 second private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(2000); 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 类,通过类 Timer 来进行节流。
因为 Hub 实例每次都会重新创建,所以只能创建一个 Broadcaster 的单例模型。调用客户端 UpdateShape 的方法被移出了 hub 。现在它通过类 Broadcaster 来管理,通过名为 _broadcastLoop 的 timer 每两秒更新一次。
最后因为不能直接在 hub 里调用客户端方法,类 Broadcaster 需要通过 GlobalHost 来获取到当前进行操作的 hub。
最终使用 F5 进行调试,虽然客户端设置了半秒一次的刷新,但是因为服务器端设置了2秒一次刷新,所以你在当前浏览器里移动 Shape ,两秒钟过后另一个浏览器里的 Shape 才会移动到当前位置。
源代码链接:
链接: https://pan.baidu.com/s/1o8NXwTW 密码: 5r5i
参考链接:
https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr