SignalR is an open source web library developed by the Microsoft ASP.NET team. It is used for creating real-time communications in web applications. In this article, I’ll give a broad view of SignalR and describe why and when to use it.
SignalR can be used wherever a user would be required to refresh a page in order to see up-to-date data. It allows the server to logically ‘push’ data to the client. This is typically required for web-based dashboards and monitoring tools where information needs to be kept up-to-date at all times without the user having to refresh the page. SignalR is a powerful, high-level library that abstracts a lot of the underlying complicated technologies in order to provide an easy way to transmit data between the client and the server. SignalR manages the connections automatically and allows data to be sent using either broadcasts or unicasts.
In SignalR, there are two distinct models for implementing client-server communications:
- Persistent Connections are the base class with an API for exposing a SignalR service over HTTP. They are useful for when developers need direct access to the low-level communication technology. Persistent connections use a similar model to that of WCF (Windows Communication Foundation).
- Hubs are built on top of Persistent Connections and abstract most of the underlying complexity in order to allow developers to call methods both on the client and the server without worrying about the implementation details. One great benefit of using Hubs is that you get model binding and serialization straight out of the box.
Transport Technologies and Fallbacks
SignalR is able to accommodate both new and older technologies on the client and the server by selecting the most appropriate communication protocol to use.
- WebSockets is the preferred protocol because it’s the most efficient, uses less memory on the server, and presents the lowest latency. To use WebSockets on the latest version of SignalR (version 2) the server component needs to run on Windows 8/Windows Server 2012 or later with .NET Framework 4.5 installed. The client needs to run on the latest version of IE, Firefox and Chrome browsers that support the HTML5 standard.
- Server-Sent Events or EventSource is used when the browser doesn’t support WebSockets. This technology is supported by all browsers apart from IE.
- Forever Frame , employed exclusively by IE, makes use of a hidden iFrame on the browser which is used to perform incomplete HTTP requests to the server. The server uses this connection to send a script to the client which gets immediately executed. This is a unidirectional connection for sending data from the server to the client. The client needs to use a separate connection to communicate with the server. Every time the client needs to send data, a new connection is created.
- Ajax long polling (also known as Hanging GET or Comet) is used as the last resort when none of the previous options can be used. Long Polling doesn’t rely on a persistent connection and instead uses requests to poll the server until the server responds. Upon receiving a response, the client closes the old connection and immediately establishes a new one. Consequently, there is quite a bit of overhead and latency involved with this solution but it guarantees that it will work with any client.
The SignalR team has done well in allowing the components to run on a variety of server and client configurations. This section provides a quick breakdown of server and client supported versions.
SignalR version 2 requires .NET Framework 4.5 while earlier versions (pre-v2.0.1) require .NET 4. The server components can be hosted in the following desktop and server operating systems:
- Windows Server 2012
- Windows Server 2008 r2
- Windows 8
- Windows 7
- Windows XP (pre-v2.0.1 due to lack of support for .NET 4.5)
- Windows Azure
Client SupportSignalR can be used both on web and desktop applications. The web library has a dependency on the jQuery library and if there is a need to use older versions of SignalR, then it’s important to note that there is also a need to use a specific version of jQuery. NuGet should resolve these dependencies automatically. Besides the tight coupling with the jQuery library, the following Web browsers are supported:
- Microsoft Internet Explorer versions 8, 9, 10, and 11. Modern, Desktop, and Mobile versions are supported
- Mozilla Firefox: current version – 1, both Windows and Mac versions
- Google Chrome: current version – 1, both Windows and Mac versions
- Safari: current version – 1, both Mac and iOS versions
- Opera: current version – 1, Windows only
- Android browser
Unsurprisingly, SignalR was designed to run equally well on mobile devices. For native applications developed with Xamarin and C#, there is a cross-platform (Windows, iOS, Android) SignalR component. The library is also natively supported on Windows Phone and Windows Store apps. Finally, most modern mobile web browsers also support SignalR.
Performance and Scalability
The default model for SignalR is a one-to-many configuration where one server can connect and handle multiple clients. However, at some point, the server may become a bottleneck if there are too many connected clients and messages. At this point there are a few implementation settings that can be tweaked to deal with application performance issues:
- Throttling message frequency
- Reducing message size (code changes)
- DefaultMessageBufferSize setting
- Max Concurrent Requests per app settings
- Application Pool Queue Length
- Max Concurrent Requests per CPU (ASP.NET specific)
- Request Queue Limit (ASP.NET specific)
However, some of these settings and code implementations are not guaranteed to solve all the performance issues, and it’s likely that the application may still be struggling. In this case, there are two further options:
- Scale-up by moving the application to a server with better hardware, more memory, and faster connection
- Scale-out by deploying concurrent server nodes that host the server component
You can get breathing space by scaling up, and it will possibly resolve the performance issues. In addition, if the SignalR application is running on the cloud and there is a specific, proven, pattern that shows utilization (CPU and Memory) spikes during certain periods throughout the day, then a scheduled scale-up/down can be implemented to reduce the costs of running on more expensive hardware. Nonetheless, there is always the possibility that this is only a temporary patch and the performance issues may outgrow the suggested solution. What is more, with one server handling the entire load, there’s still the risk that comes from having a single point of failure. This is where there is an advantage from scaling out.
Instead of throwing bigger, faster, hardware to the problem, you can use multiple, concurrent nodes to share the load and provide both redundancy and stability to the platform. Scaling out introduces a new problem because the clients can get routed to any server at any time, thereby breaking the connection and message continuity. To address this issue, SignalR needs to use a backplane. The backplane component is used to keep all servers in sync so if Client-A sends a message to Server-A, then a response may be received by any of the servers connected to the backplane as per the example below:
SignalR supports one of three scale-out backplanes depending on the requirements of the project:
- Azure Service Bus is a cloud messaging infrastructure that allows components to send messages in a loosely-coupled way.
- Redis is an in-memory key-value store and supports a publish/subscribe (“pub/sub”) pattern for sending messages.
- SQL Server writes messages to SQL tables. The backplane can optionally use the Service Broker to improve the efficiency of messaging.
Azure Service Bus can only run on Azure. Redis can be deployed to any standalone server (Windows or Linux). However, there is an option to use the Azure Redis solution to deploy the backplane on the cloud. Finally, the SQL Server backplane requires SQL Server 2005 or later and neither SQL Server Compact, Express nor SQL Azure are supported.
Connections are made persistent for the life of the session. Both server-hub and client have internal implementations for managing the connections automatically. By abstracting and hiding away these implementations, SignalR scores points both in simplicity and usability. Nonetheless, if you need more, the API has a way of exposing connection events to enable developers to respond accordingly in the code.
Connection timeout settings
- ConnectionTimeout is set to 110 seconds by default and represents the amount of time to leave a connection open and waiting for a response before closing it down and opening a new one. This setting only applies to the long polling transport.
- DisconnectTimeout is set to 30 seconds by default and represents the amount of time to wait after a transport connection is lost.
- KeepAlive is set to 10 seconds by default and is always set to 1/3 of the DisconnectTimeout. To change the default value, the setting needs to be set after the DisconnectTimeout otherwise it will be overwritten. The KeepAlive represents the time to wait before sending keepalive packet over an idle connection.
Client-Side Connection Events
SignalR exposes the following client-side connection API methods:
- starting : Raised before any data is sent over the connection
- received : Raised when any data is received on the connection – provides the received data
- connectionSlow : Raised when the client detects a slow or frequently dropping connection
- reconnecting : Raised when the underlying transport begins reconnecting
- reconnected : Raised when the underlying transport has reconnected
- stateChanged : Raised when the connection state changes – provides the old state and the new state (connecting, connected, reconnecting, or disconnected)
- disconnected : Raised when the connection has disconnected
Server-Side Connection Events
The server doesn’t have a lot of control over the connection state. There is only one method that accepts a Boolean parameter to indicate whether the connection was dropped by the client or if the client purposefully disconnected:
- OnDisconnected ( bool stopCalled ) : raised when a connection is dropped. If stopCalled is set to true it means that the client has requested to disconnect. Otherwise, it’s assumed that the connection was interrupted and the DisconnectTimeout period has elapsed.
For some applications, connections must be mapped to specific users. A legitimate case would be to map user login credentials to the SignalR connectionID in order to route messages to specific users for security and integrity purposes. SignalR version 2 can manage and persist user and connection mapping using 4 different approaches:
- User ID Prov ider
- In-memory storage such as a dictionary or Hashtable
- SignalR group for each user
- Permanent external storage such as a database table or Azure table storage
The table below provides a breakdown on the benefits of each approach:
|More than one server||Get list of currently connect users||Persist information after restarts||Optimal performance|
If reliability and persistence are, even after restarts, important for your application, then you must use only the permanent external option. More information on SignalR connection mapping and user persistence can be found here: http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections .
We have looked into the different features of SignalR and discussed what makes this library so powerful and versatile. SignalR is excellent for implementing robust real-time communications that perform well on web, desktop and mobile applications. In the next article, we will look into how to set up – and start using – SignalR applications in our code.
- SignalR Official Website: http://www.asp.net/signalr/overview/getting-started/introduction-to-signalr
- SignalR Source Code on Github (Open Source): https://github.com/SignalR/SignalR
- Book Real-Time Communication in .NET with SignalR 2.1: https://www.apress.com/9781430263197