Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Introduction to SignalR

0.00/5 (No votes)
26 Feb 2015 1  
Introduction to using SignalR with ASP.NET

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:

C#
$.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.

880963/SignalRComputers1.png

880963/SignalRComputers2.png

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:

880963/AddSignalRToProject.png

Selecting SignalR:

880963/MicrosoftSignalR.png

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:

C#
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:

C#
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; }
    }
}      

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:

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>

Also, ensure that your web.config file contains the following:

XML
<?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>

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:

C#
<!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> 

Add Server Loop

Adding the following code to your hub also reduces traffic on the server side:

C#
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; }
    }
}

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:

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;
                        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> 

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here