SignalR 2.0
.NET client
server hub
best practice
reconnection

Best practice for reconnecting SignalR 2.0 .NET client to server hub

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

SignalR 2 clients can reconnect automatically in some transport-level interruption cases, but that does not remove the need for application-level recovery logic. A real reconnect strategy has to answer three separate questions: when do you try to reconnect, how aggressively do you retry, and what state must be restored once the connection comes back. If you only call Start() again blindly, you usually end up with noisy logs and fragile behavior.

Understand What SignalR Reconnects Automatically

SignalR 2 distinguishes between a transient transport interruption and a fully closed connection. During transient loss, the client may move through reconnecting events and recover without you rebuilding everything manually. If the connection closes completely, you need to create or restart it yourself.

The client-side events are useful for visibility:

  • 'ConnectionSlow'
  • 'Reconnecting'
  • 'Reconnected'
  • 'Closed'

A basic setup looks like this:

csharp
1using Microsoft.AspNet.SignalR.Client;
2using System;
3using System.Threading.Tasks;
4
5class ClientApp
6{
7    private readonly HubConnection _connection;
8    private readonly IHubProxy _hub;
9
10    public ClientApp(string url)
11    {
12        _connection = new HubConnection(url);
13        _hub = _connection.CreateHubProxy("ChatHub");
14
15        _connection.ConnectionSlow += () => Console.WriteLine("Connection is slow.");
16        _connection.Reconnecting += () => Console.WriteLine("Reconnecting...");
17        _connection.Reconnected += () => Console.WriteLine("Reconnected.");
18        _connection.Closed += async () => await RestartWithBackoffAsync();
19    }
20
21    public Task StartAsync() => _connection.Start();
22
23    private async Task RestartWithBackoffAsync()
24    {
25        var delay = TimeSpan.FromSeconds(2);
26
27        while (_connection.State == ConnectionState.Disconnected)
28        {
29            try
30            {
31                await _connection.Start();
32                Console.WriteLine("Connection restarted.");
33                return;
34            }
35            catch
36            {
37                await Task.Delay(delay);
38                delay = TimeSpan.FromSeconds(Math.Min(delay.TotalSeconds * 2, 30));
39            }
40        }
41    }
42}

The important part is not the exact delay values. It is the separation between event handling and retry policy.

Use Backoff, Not Tight Loops

A reconnect loop should not hammer the server every few milliseconds. Exponential backoff is a better default because it reduces pressure during outages and gives infrastructure time to recover.

You should also log each attempt and surface connection state to the rest of the application. That allows the UI or calling service to behave differently when the hub is offline instead of pretending everything is fine.

Re-Subscribe and Restore State

Reconnecting transport is not always enough. After a full disconnect, your application may need to restore state such as:

  • hub method subscriptions
  • group membership
  • pending requests
  • authentication context
  • local caches or UI indicators tied to connectivity

That restoration step is where many reconnect implementations fail. The socket comes back, but the app behaves as if nothing happened even though important hub state was lost.

A good pattern is to centralize connection setup and reconnection in one class so the logic for event wiring, starting, and post-reconnect restoration lives in one place.

Avoid Duplicate Start Calls

One subtle bug is calling Start() from several places at once. If a timer, a Closed handler, and some external "connect" button all try to restart the same connection, the client can get into inconsistent states or produce hard-to-read errors.

Guard reconnect attempts so only one restart flow runs at a time. A semaphore or dedicated state machine is often enough.

Common Pitfalls

  • Assuming SignalR's built-in reconnect behavior covers every disconnect scenario.
  • Retrying immediately in a tight loop and overwhelming the server during outages.
  • Forgetting to restore subscriptions, groups, or UI state after reconnection.
  • Calling Start() concurrently from multiple places.
  • Treating reconnect logs as noise instead of using them to understand real connection health.

Summary

  • SignalR 2 can handle some transient reconnects automatically, but full recovery still needs application logic.
  • Use connection events for visibility and a controlled backoff policy for retries.
  • Restore hub-related state after a real disconnect, not just the transport.
  • Centralize connection and reconnection logic so it stays consistent.
  • Prevent overlapping Start() calls, because reconnect races create subtle bugs.

Course illustration
Course illustration

All Rights Reserved.