Real-time communication is an important part of modern web applications. Features such as live chat, notifications, dashboards, online collaboration, and multiplayer applications depend on maintaining an active connection between the client and server.
ASP.NET Core SignalR makes this communication easier by allowing the server to send messages to connected clients without requiring them to continuously send HTTP requests.
However, real-world network connections are not always reliable. A user might switch from Wi-Fi to mobile data, lose internet connectivity temporarily, close a laptop, or experience a server restart. In these situations, the SignalR connection can be interrupted.
Without proper reconnection handling, users may stop receiving notifications, chat messages, or live updates without understanding what happened.
SignalR provides automatic reconnection support, but developers still need to manage connection states, retry attempts, and application data synchronization.
In this article, we will explore how network disconnections happen in SignalR applications, implement automatic reconnection, configure retry policies, and handle reconnection scenarios in production applications.
Understanding Network Disconnections in SignalR
SignalR establishes a persistent connection between a client and a server using an available transport.
It supports the following transport options:
- WebSockets
- Server-Sent Events
- Long Polling
WebSockets is generally preferred when both the client and server support it. SignalR can negotiate an appropriate transport based on the environment and server capabilities.
However, regardless of the transport, the connection can be interrupted.
Some common reasons include:
- Temporary internet connectivity problems.
- Switching between Wi-Fi and mobile networks.
- Server restarts or application deployments.
- Reverse proxy or load balancer connection timeouts.
- Browser tabs becoming inactive or suspended.
- Network interruptions caused by firewalls or intermediate infrastructure.
Consider a chat application where a user is connected to a SignalR hub.
When the internet connection disappears, the client may no longer be able to communicate with the server. If the application does not handle this situation, the user might continue typing messages even though the connection is no longer active.
A reliable implementation should detect the disconnection, notify the user, attempt reconnection, and restore any application state that was lost.
Understanding SignalR Connection States
SignalR provides a connection state property that helps developers understand the current connection status.
The JavaScript client exposes the following states:
Connection state | Description |
|---|
Connecting | The client is establishing a connection |
Connected | The connection is active |
Reconnecting | The client is attempting to restore a lost connection |
Disconnected | The connection is no longer active |
These states are useful when updating the user interface.
For example, a chat application can disable its message input while reconnecting and display a message indicating that the connection is being restored.
It is important to distinguish between a temporary reconnection attempt and a completely disconnected connection. They represent different situations and should be handled separately.
Implementing Automatic Reconnection in SignalR
SignalR provides a built-in automatic reconnection feature through the withAutomaticReconnect method.
One important detail is that automatic reconnection is not enabled by default.
Let's implement it using the JavaScript SignalR client.
Step 1. Install the SignalR Client Library
If your application uses npm, install the SignalR client package.
npm install @microsoft/signalr
Step 2. Create the SignalR Connection
Create a connection to your ASP.NET Core hub.
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Information)
.build();
The withUrl method specifies the hub endpoint.
The withAutomaticReconnect method enables automatic reconnection when an established connection is interrupted.
Without additional configuration, SignalR uses the following retry delays:
Attempt | Waiting time |
|---|
First attempt | Immediately |
Second attempt | 2 seconds |
Third attempt | 10 seconds |
Fourth attempt | 30 seconds |
After four unsuccessful reconnection attempts, the client stops automatically reconnecting.
This default configuration is suitable for applications where temporary network interruptions are expected, but applications with different availability requirements may need a custom retry policy.
Step 3. Handle Reconnection Events
SignalR provides three important lifecycle callbacks:
- onreconnecting
- onreconnected
- onclose
These callbacks allow the application to respond to changes in connection status.
connection.onreconnecting(error => {
console.log("Connection lost. Reconnecting...");
document.getElementById("connectionStatus")
.textContent = "Reconnecting...";
document.getElementById("sendButton").disabled = true;
});
connection.onreconnected(connectionId => {
console.log("Connection restored:", connectionId);
document.getElementById("connectionStatus")
.textContent = "Connected";
document.getElementById("sendButton").disabled = false;
});
connection.onclose(error => {
console.log("Connection permanently closed:", error);
document.getElementById("connectionStatus")
.textContent = "Disconnected";
document.getElementById("sendButton").disabled = true;
});
Each callback serves a different purpose.
The onreconnecting callback runs when SignalR starts trying to restore an established connection.
The onreconnected callback runs when the connection is successfully restored.
The onclose callback runs when the connection is closed and automatic reconnection has stopped or the connection is otherwise closed.
This makes it easier to provide meaningful feedback instead of allowing users to assume that the application is still connected.
Step 4. Start the Connection
The automatic reconnection feature does not handle initial connection failures.
For example, if the application starts while the server is unavailable, the initial start operation can fail. You need to implement retry handling separately.
async function startConnection() {
try {
await connection.start();
console.log("SignalR connected successfully.");
document.getElementById("connectionStatus")
.textContent = "Connected";
} catch (error) {
console.error("SignalR connection failed:", error);
document.getElementById("connectionStatus")
.textContent = "Unable to connect. Retrying...";
setTimeout(startConnection, 5000);
}
}
startConnection();
This implementation retries the initial connection after five seconds if the attempt fails.
For a production application, consider using an exponential backoff strategy instead of retrying at a fixed interval. Also, prevent multiple retry loops from running simultaneously.
Configuring a Custom Reconnection Policy
The default retry schedule might not be suitable for every application.
For example, a real-time dashboard may need to keep trying for a longer period, while a short-lived interactive session may stop reconnecting after a limited number of attempts.
SignalR allows developers to configure custom retry delays.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds > 120000) {
return null;
}
const delay = Math.min(
1000 * Math.pow(
2,
retryContext.previousRetryCount
),
30000
);
return delay;
}
})
.build();
This policy uses exponential backoff.
The waiting time increases after each unsuccessful attempt, up to a maximum of 30 seconds.
The policy stops retrying when the total elapsed reconnection time exceeds two minutes.
Returning null tells SignalR to stop automatic reconnection.
Exponential backoff is useful because it prevents clients from continuously sending connection requests when the server is unavailable.
For applications with many simultaneous clients, adding random jitter to retry delays can further reduce the possibility of all clients reconnecting at the same time.
Implementing Automatic Reconnection in a .NET Client
SignalR is not limited to browser applications. You can also use it in desktop applications, background services, and other .NET clients.
The .NET client provides similar functionality through WithAutomaticReconnect.
First, create a connection:
using Microsoft.AspNetCore.SignalR.Client;
var connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7001/chathub")
.WithAutomaticReconnect()
.Build();
Next, handle connection lifecycle events.
connection.Reconnecting += error =>
{
Console.WriteLine(
"Connection lost. Attempting to reconnect.");
return Task.CompletedTask;
};
connection.Reconnected += connectionId =>
{
Console.WriteLine(
"Connection restored successfully.");
return Task.CompletedTask;
};
connection.Closed += error =>
{
Console.WriteLine(
"SignalR connection closed.");
return Task.CompletedTask;
};
Finally, start the connection.
await connection.StartAsync();
The .NET client follows the same default retry schedule as the JavaScript client.
However, just like the JavaScript implementation, WithAutomaticReconnect does not retry failures from the initial StartAsync operation. Initial connection retries must be implemented separately.
Restoring Application State After Reconnection
Successfully reconnecting to SignalR does not automatically restore every part of the application's previous state.
This is an important consideration for production applications.
For example, consider a notification dashboard that subscribes to a group based on the user's account ID.
When the connection is restored, SignalR establishes a new connection. The new connection is treated as a separate connection on the server, and its connection ID changes.
Therefore, the application should restore any required group memberships.
You can handle this inside the onreconnected callback:
connection.onreconnected(async () => {
await connection.invoke(
"JoinUserGroup",
currentUserId
);
await refreshNotifications();
});
The server can define a hub method for joining the required group:
public class NotificationHub : Hub
{
public async Task JoinUserGroup(string userId)
{
await Groups.AddToGroupAsync(
Context.ConnectionId,
$"user-{userId}");
}
}
The application also refreshes notifications after reconnecting.
This is important because messages sent while the client was disconnected may not have been received.
For reliable applications, do not assume that reconnecting means no messages were missed. Use a database-backed message history, event sequence numbers, or another synchronization mechanism to retrieve missing information.
Also, validate the user's identity and authorization on the server. Do not trust a client-provided user ID alone when assigning group membership.
Advantages and Disadvantages of Automatic Reconnection
Advantages
- Improved user experience: Users do not need to manually refresh the application after every temporary network interruption.
- Reduced manual implementation: SignalR handles retry scheduling and connection lifecycle transitions.
- Flexible retry policies: Developers can configure retry delays and maximum retry durations.
- Better application availability: Temporary network interruptions can be recovered without restarting the entire application.
- Lifecycle notifications: Applications can update the user interface based on connection status.
Disadvantages
- No guaranteed message delivery: Messages sent while the client is disconnected may be missed.
- Initial connection failures require separate handling: Automatic reconnection only handles interruptions after a successful connection.
- Additional server load: Many clients reconnecting simultaneously can create a temporary increase in server traffic.
- Application state must be restored: Group memberships and other connection-specific information may need to be established again.
- Retry limits: The default policy stops after four failed attempts, which may not satisfy every application's requirements.
Best Practices for Handling SignalR Disconnections in Production
Display Connection Status to Users
Always provide clear feedback when a real-time connection is interrupted.
For example, a chat application can display Connecting, Reconnecting, Connected, or Disconnected.
This prevents users from assuming that their messages or actions are being processed.
Use Exponential Backoff
Avoid continuously retrying connection attempts at very short intervals.
Exponential backoff reduces unnecessary traffic and gives the server time to recover.
Add jitter when many clients may reconnect simultaneously.
Restore Group Memberships
If your application uses SignalR groups, rejoin the required groups after reconnection.
Do not assume that the previous connection's group membership automatically transfers to the new connection.
Synchronize Missed Messages
Automatic reconnection does not provide guaranteed delivery of every message sent during a disconnection.
Retrieve missed notifications or messages from a reliable server-side source after reconnecting.
Make Operations Idempotent
A client may not know whether an operation completed before the connection was interrupted.
For important operations, use unique request identifiers or idempotency keys so that retrying does not accidentally create duplicate payments, orders, or transactions.
Monitor Connection Failures
Log connection interruptions, retry attempts, successful reconnections, and permanent disconnections.
These details help identify network problems, server instability, and infrastructure configuration issues.
Common Mistakes and Troubleshooting
Problem | Possible reason | Recommended solution |
|---|
SignalR does not reconnect automatically | WithAutomaticReconnect was not configured | Enable automatic reconnection |
Initial connection keeps failing | Automatic reconnection does not retry initial start failures | Implement a separate initial connection retry mechanism |
Messages are missing after reconnection | Messages were sent while the client was disconnected | Retrieve missed messages from persistent storage |
Group messages are no longer received | New connection does not automatically restore group membership | Rejoin required groups after reconnection |
Server receives too many reconnect requests | Clients retry too frequently | Use exponential backoff and jitter |
Connection closes repeatedly | Proxy timeout, network instability, or server interruptions | Review server logs, transport configuration, and infrastructure timeouts |
Duplicate operations occur | Client retries an operation without knowing whether the first attempt succeeded | Use idempotency keys and server-side deduplication |
Summary
Network interruptions are unavoidable in real-time applications. A SignalR connection can be lost because of internet problems, server restarts, proxy timeouts, or inactive browser tabs.
ASP.NET Core SignalR provides automatic reconnection through WithAutomaticReconnect, allowing applications to recover from temporary connection failures without requiring users to refresh the page.
However, reconnection is only one part of a reliable real-time communication strategy. Applications must also handle initial connection failures, manage connection states, restore group memberships, and synchronize messages that may have been missed.
For production applications, combine automatic reconnection with exponential backoff, proper error handling, persistent message storage, and connection monitoring.
This approach helps build SignalR applications that remain usable even when network conditions are unreliable.
Join the conversation! Your thoughts help the community grow.