Introduction
This tip covers introductory information on real-time communications using SignalR, in the context of a web application, where the server can push information to clients as soon as it becomes available rather than waiting for the client to request data from the server.
SignalR is useful for chat applications, dashboards or games in terms of the world wide web, but is also essential for industrial desktop applications.
If you are using AJAX to get your web application to be more responsive, then SignalR is a useful alternative.
You can determine what type of transport SignalR is using by enabling logging:
$.connection.hub.logging = true;
If you then open the debugging tools on your web browser, you will see a line such as "SignalR: Websocket opened".
What is SignalR?
Normally, to get more data on the client from the server if it has changed, you have to perform polling with HTTP GET
, which is what AJAX does. If you want anything near realtime, then you have to poll quite frequently. Instead, with SignalR, you have a persistent connection where the client can call the server and the server can call the client.
Therefore, SignalR is a library for developers that simplifies real-time communications, where the server and client connection is persisted, unlike the traditional HTTP paradigm.
If your computer is using Windows 8 or Windows Server 2012 or newer, and the .NET Framework 4.5, then SignalR will use the WebSocket; otherwise, it will use HTML 5 transports or Comet transports.
Connections and Hubs
The two models for communicating between the clients and servers are Persistent Connections and Hubs.
Connections represent endpoints for sending single-recipient, grouped or broadcast messages.
A Hub is a layer on top of Persistent Connections that allows your client and server to call methods on each other directly. This model will be familiar to .NET developers who have used .NET Remoting. This allows you to pass strongly-typed parameters.
When full objects are sent via the communication channel, they are serialized using JSON.
You can monitor method calls using tools like Fiddler.
Creating Application
Start by creating an empty ASP.NET web project, right click to add SignalR, ensuring your project is targeting .NET 4.5 or higher:
Selecting SignalR:
Or you can just type this at the package manager console:
PM> install-package Microsoft.AspNet.SignalR
PM> install-Package jQuery.UI.Combined
PM> install-Package Microsoft.Web.Infrastructure
Next, add a class called Startup
to your application, that maps your SignalR
hub when the application starts:
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();
}
}
}
If you haven't already, then add the following SignalR
hub class:
using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
namespace CodeProjectSignalRSample
{
public class MyHub : Hub
{
public void UpdateModel(ShapeModel clientModel)
{
clientModel.LastUpdatedBy = Context.ConnectionId;
Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
}
}
public class ShapeModel
{
[JsonProperty("left")]
public double Left { get; set; }
[JsonProperty("top")]
public double Top { get; set; }
[JsonIgnore]
public string LastUpdatedBy { get; set; }
}
}
Clients can call the UpdateModel
method directly, which then gets broadcasted to all connected clients. It gets automatically serialized using JSON.
Now add the client, by adding an HTML page to your solution:
<!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>
Also, ensure that your web.config file contains the following:
="1.0"="utf-8"
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration>
Start the application, then copy the URL into a new instance of the browser. Try moving the shape around.
Add Client Loop
Sending the location on every mouse move creates a lot of network traffic, so you can throttle it by putting this code in your HTML file:
<!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"),
messageFrequency = 10,
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;
}
});
setInterval(updateServerModel, updateRate);
});
function updateServerModel() {
if (moved) {
moveShapeHub.server.updateModel(shapeModel);
moved = false;
}
}
});
</script>
<div id="shape" />
</body>
</html>
Add Server Loop
Adding the following code to your hub also reduces traffic on the server side:
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());
private readonly TimeSpan BroadcastInterval =
TimeSpan.FromMilliseconds(40);
private readonly IHubContext _hubContext;
private Timer _broadcastLoop;
private ShapeModel _model;
private bool _modelUpdated;
public Broadcaster()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<myhub>();
_model = new ShapeModel();
_modelUpdated = false;
_broadcastLoop = new Timer(
BroadcastShape,
null,
BroadcastInterval,
BroadcastInterval);
}
public void BroadcastShape(object state)
{
if (_modelUpdated)
{
_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;
Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
}
}
public class ShapeModel
{
[JsonProperty("left")]
public double Left { get; set; }
[JsonProperty("top")]
public double Top { get; set; }
[JsonIgnore]
public string LastUpdatedBy { get; set; }
}
}
Add Smooth Animation to Client
Add the following code to cause the shape to move from the old location to the new one over the course of 1000 milliseconds:
<!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"),
messageFrequency = 10,
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;
$shape.animate(shapeModel, { duration: updateRate, queue: false });
}
});
setInterval(updateServerModel, updateRate);
});
function updateServerModel() {
if (moved) {
moveShapeHub.server.updateModel(shapeModel);
moved = false;
}
}
});
</script>
<div id="shape" />
</body>
</html>
References