SignalR Programming in Microsoft ASP.NET

SignalR Programming in Microsoft ASP.NET

More About this Book

Chapter 2. HTTP: You are the client, and you are the boss


Pull Communications Model

  • HTTP's Request-Response form of communication is an example of a Pull model where the client initiates the interaction to the server
  • Polling is traditionally the preferred strategy to achieve real-time bidirectional communication in HTTP. Implementations can be of:
    • Basic Polling where requests are sent based on defined time intervals
    • Adaptive Polling where the interval is adjusted based on the server load
    • Piggyback polling where request #2 is sent along with request #1, where request #1 is triggered by a UI interaction

Push Communications Model

  • This form of communications is used for real-time communications like in chat services, collaboration tools, and broadcast services where clients subscribe to a server
  • Some protocols that support this model are SMTP, IRC, WebSockets and Server-Sent Events in HTML5
  • WebSockets is different from Server-Sent Events (SSE) in that the former is bidirectional and the latter is one-directional. SSE runs on the HTTP protocol while WebSockets has its own ws protocol. In SSE, if you need to send a different request, you have to create a different HTTP connection.
  • Without HTML5’s WebSockets or SSE, push communications can be mimicked in HTTP via these Comet application model polling techniques
    • Long-polling where the server connection once initiated by the client, remains open until it sends a response or if it times out
    • Forever frame where an IFRAME is embedded in the page and its SRC attribute pointing to a server url which is kept open permanently

 

Chapter 3. Introducing SignalR

What SignalR Offers
SignalR as a component in a .NET web application offers server and client-side solutions for persistent communication.

SignalR vs WebSocket
SignalR provides abstraction for persistent server connections. One of those is WebSocket, then there are also Server-Sent Events, Forever Frame and Long Polling. You have the option to select which transport protocol to use or let it pick the best one for you automatically.

 

Chapter 4. Persistent connections

SignalR library’s API abstraction
SignalR Abstraction

MSDN: The PersistentConnection class Represents a connection between client and server.
It provides methods that are similar to WebSockets to help you manage users connecting in and out of your server. It is the base class from which one must inherit to expose server connection events.

Simple PersistentConnection example for the SignalR Server
Note that the Task object is used indicating the asynchronous nature of the methods as well as Interlocked.Increment for thread-safety

public class VisitorsCountConnection: PersistentConnection
{
    private static int connections = 0;
    protected override Task OnConnected(IRequest request, string connectionId)
    {
        Interlocked.Increment(ref connections);
        return base.OnConnected(request, connectionId);


        // Or notify all connected clients using Broadcast()
        // return this.Connection.Broadcast('New connection: ' + connectionId);

        // Or only notify the connecting client using Send()
        // return this.Connection.Send(connectionId, 'Welcome');
    }

    // Use async and await on any of the PersistentConnection methods if you need to 
    // to handle a long running task
    protected override async Task OnReceived(IRequest request, string connectionId, string clientData)
    {
        // Convert clientData string into a type
        var message = JsonConvert.DeserializeObject<ClientMessage>(clientData);

        await this.Connection.Send(connectionId, "Process starting");
    }

    protected override Task OnDisconnected(IRequest request, string connectionId)
    {
        Interlocked.Decrement(ref connections);
        return base.OnDisconnected(request, connectionId);
    }


    // Other methods can be found on this link such as the 2 in comment
    // https://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.persistentconnection_methods(v=vs.118).aspx
    // protected override Task OnReconnected
    // protected override Task OnRejoiningGroups
}

Mapping a PersistentConnection to a URL path for the client
SignalR connection mapping is created inside the StartUp class.

public class StartUp
{
    public void Configuration(IAppBuilder app)
    {
        // Use GlobalHost for SignalR configuration
        // https://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.globalhost(v=vs.118).aspx
        Microsoft.AspNet.SignalR.GlobalHost
                .Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);


        // where MyConnection inherits from PersistentConnection
        app.MapSignalR<MyConnection>("/my/path");

        // For older browsers with no JSON support, enable JSONP
        // var config = new ConnectionConfiguration(){EnableJSONP = true};
        // app.MapSignalR<MyConnection>("/my/path", config);

        /*
            // Enabling CORS
            // Microsoft.Owin.Cors must be referenced in the project 
            // and namespaced in the Startup.cs file 
            app.Map("/my/path",
                map =>
                {
                    map.UseCors(CorsOptions.AllowAll);
                    map.RunSignalR<MyConnection>();  // same as MapSignalR(...)
                }
            );
        /*
    }
}

Grouping clients and broadcasting to them
When sending messages to a group of clients, your client application can send your SignalR server with a JSON string payload containing its grouping information. Then you can parse that argument from your SignalR server to group your messages

protected override Task OnReceived(IRequest request, string connectionId, string data)
{
    // data argument structure will vary by implementation
    var args = data.Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries);
    if(args.Length == 2 && args[0].ToLower()=="GROUPKEY")
    {
        return this.Groups.Add(connectionId, args[1]);
    }
}

Minimum Javascript requirements for client side applications connecting to a SignalR server

Simple client example using the SignalR Server PersistentConnection
Code breakdown of the client Javascript for communicating with the SignalR server. Use the PersistentConnection example above for your server reference

var connection = $.connection('/my/path');

// Enable console logging
connection.logging = true;

// start() is asynchronous
connection.start()
    .done(function () { })
    .fail(function () { });

// Sending data to the SignalR Server
$('#someButton').click(function () {
    // This will be submitted to OnReceived method
    connection.send({ key: "value" });
});

// Receiving data from the SignalR Server
connection.received(function (msg) {
    // This will be called after a
    // SignalR Server Broadcast() or Send()
    $('body').append(msg + '<br />');
});

// Other client events
// https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#connectionlifetime

 

Chapter 5. Hubs

A Hub is a more high-level pipeline built upon the Connection API that allows your client and server to call methods on each other directly
The Hub API gives you access to the Persistent Connection from the client or the server. It follows a naming convention to map RPC’s between server and client.

Simple Hub example for the SignalR Server
Note that every client request will create an instance of a Hub and the instance eliminated as soon as the request is processed

// Use this attribute to override the class name of the hub
[HubName("HubNameOverride")]
public class MyHub : Hub
{
   // Use this attribute to change the method name
   // the client will use
   [HubMethodName("method1")]
   public void MyMethod(string name)
   {
       // Hub through HubBase has a Context property that will
       // give you access to properties related to the clients request
       string connectingUser = Context.User.Identity.Name;

       // Hub object has a Clients property you can use to
       // reference clients connecting to the SignalR server.
       // Note that Clients is dynamic type where myArbitraryMethod
       // is the client function to invoke      
       Clients.All.myArbitraryMethod(name, "Welcome " + connectingUser);
   }

   // Type-checking between client and server is done for you by the library.
   // Overloads are acceptable but note that methods with different signatures
   // will cause ambiguity because the library only uses the number of
   // method arguments when mapping the client request.
   public void MyMethod(string name, int args2)
   {
       // Example of passing state from the client
       // Client.js: hub.state.EmployeeName = 'John Doe'
       Clients.Caller.handleResponse(string.Format("Hello {0}", Clients.Caller.EmployeeName));
   }

   // Async-await is supported
   public async Task<int> Foo()
   {
       return await Something();
   }

   private Task<int> Something()
   {
       throw new NotImplementedException();
   }
}

Mapping a PersistentConnection to a URL path for the client

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // You can mix persistent connections with hubs
        // app.MapSignalR<MyPersistentConnection>('/myconnection');

        // Map Hub Connection

        // This maps your hub to the /signalr path by default
        app.MapSignalR();

        // Use the overload constructor if you want to use a different path
        // app.MapSignalR("/mypath", new HubConfiguration(){
              // Specifies whether you want an automated generation of the
              // client javascript proxy to the hub
              // EnableJavaScriptProxies = true; // default
        // });
    }
}

Minimum Javascript requirements for client side applications connecting to a SignalR server

Simple client example using the SignalR Server Hub from this chapter
This example uses the auto-generated proxy file.

// Note that the client uses camel-casing while the server uses Pascal

// To enable console logging in the client
$.connection.hub.logging = true;

// reference your hub name from the `connection` object
var myHub = $.connection.hubNameOverride;

// Establish your connection
$.connection.hub.start()
   .done(function () {
       // You can invoke any SignalR server method
       // through the `server` object
       document.getElementById("test").click = function () {
           myHub.server.myMethod("John Doe");
       };
   })
   .fail(function (e) { console.log(e); });


// This is the client script that gets executed after the method
// MyHub.MyMethod() function is called on the SignalR Hub
myHub.client.myArbitraryMethod = function (message) {
   console.log(message);
}

This example does NOT use the auto-generated proxy file.

var connection = $.hubConnection();

connection.logging = true;

var myHub = connection.createHubProxy('hubNameOverride');

$.connection.start()
   .done(function () {
       document.getElementById('test').click = function () {
           // client calling a server method
           myHub.invoke('myMethod', 'John Doe');
       };
   })
   .fail(function (e) { console.log(e); });

// server will call this client method
myHub.on('myArbitraryMethod', function (message) {
   console.log(message);
});

You are better off not using the auto-generated proxy files
Working with the auto-generated proxy file is very convenient for small projects. With larger applications, there are a couple of considerations to make:

  • Performance: It bloats the client script file, no minification
  • Security: It exposes all the server methods increasing the attack surface of your server

 

Chapter 7. Real-time multiplatform applications

Hosting options for SignalR

  • IIS - Hosting SignalR in IIS is the same as hosting a website.
  • Console Application for when IIS is not available or is too much of an overhead
  • Windows Service - reasons are the same as the Console Application but the startup requirements are different

 

Chapter 8. Deploying and scaling SignalR

Horizontally scaling SignalR servers using backplanes
SignalR employs a "backplane" to connect multiple server instances. The idea of a backplane linking connectors in a hardware board is the same - where server instances are plugged in a backplane to allow message sharing between instances. Backplanes address session management and data sharing requirements between SignalR server instances. A typical web architecture may look like the one below. For more information on backplanes, visit the Scaleout introduction from MSDN.

Backplanes

Performance Recommendations

  • Use asynchronous features for expensive or blocking calls
  • Optimize the size of the data you’re transmitting
  • Throttle message submissions whenever possible
  • Be aware of your transport (WebSocket, Long Polling, etc.)
  • Be aware of your concurrent requests (default is 5,000)
  • Use the Performance Monitor with SignalR counters to peer into your server

 

Chapter 9. Advanced topics

How do you secure your SignalR connections

  • Authentication - The job of authentication should be handled by the application of the originating request. By the time the connection hits the SignalR server, one can inspect the request.User.Identity.IsAuthenticated property to verify that the connection has been checked.
  • Authorization - use the Microsoft.AspNet.SignalR.Authorize attribute for fine-grained control of access to your resources