What SignalR Solves
SignalR is a library for ASP.NET Core that abstracts real-time, bidirectional communication between server and connected clients, so the server can push data to browsers, mobile apps, or desktop clients the instant something happens rather than clients repeatedly polling an endpoint for updates. It automatically negotiates the best available transport for the client and network — preferring WebSockets, but falling back to Server-Sent Events or long polling when WebSockets aren't available — so application code targets one consistent programming model regardless of which transport is actually used underneath.
Cricket analogy: This is like a stadium's live score notification pushed straight to your phone the instant a wicket falls, rather than you having to refresh a scorecard app every thirty seconds to check — SignalR pushes updates instead of requiring polling.
Hubs, Groups, and Methods
The central abstraction in SignalR is the Hub, a class you write that inherits from Hub and exposes public methods clients can call remotely (e.g., a SendMessage method), while the Hub itself can call back to connected clients through the strongly-typed or dynamic Clients property, targeting a single caller (Clients.Caller), everyone (Clients.All), everyone except the sender (Clients.Others), or a named subset via Groups, which you manage by calling Groups.AddToGroupAsync and Groups.RemoveFromGroupAsync, typically inside OnConnectedAsync/OnDisconnectedAsync or a join-group method the client invokes.
Cricket analogy: This is like a stadium's PA system where the announcer (the Hub) can address the whole crowd (Clients.All), a specific section (a Group, like the members' pavilion), or reply directly to one heckler who shouted a question (Clients.Caller) — one broadcast point, multiple addressable audiences.
public class ChatHub : Hub
{
public async Task SendMessage(string room, string user, string message)
{
await Clients.Group(room).SendAsync("ReceiveMessage", user, message);
}
public async Task JoinRoom(string room)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room);
await Clients.Group(room).SendAsync("ReceiveMessage", "system", $"{Context.ConnectionId} joined {room}");
}
}
// Program.cs
builder.Services.AddSignalR();
// ...
app.MapHub<ChatHub>("/hubs/chat");
// JavaScript client
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat")
.withAutomaticReconnect()
.build();
connection.on("ReceiveMessage", (user, message) => {
appendToChatLog(`${user}: ${message}`);
});
await connection.start();
await connection.invoke("JoinRoom", "general");Scaling Out with a Backplane
A single ASP.NET Core server instance can hold thousands of concurrent SignalR connections in memory, but once you scale to multiple server instances behind a load balancer, a message sent from a hub on Server A must still reach a client whose WebSocket connection happens to be pinned to Server B. SignalR solves this with a backplane, most commonly Redis via the Microsoft.AspNetCore.SignalR.StackExchangeRedis package, which republishes messages across all server instances so Clients.All, Clients.Group, and similar calls behave correctly regardless of which server a given client is connected to.
Cricket analogy: This is like multiple regional radio commentary booths (each covering local listeners) all needing to relay the same wicket announcement simultaneously, requiring a shared central feed everyone taps into, similar to a Redis backplane relaying messages across server instances.
SignalR clients automatically negotiate down from WebSockets to Server-Sent Events or long polling when needed (for example, behind certain corporate proxies), and the withAutomaticReconnect() client option lets a dropped connection retry and rejoin automatically, though your code should still handle re-joining any groups on reconnect since group membership is tied to the connection.
Do not use Context.ConnectionId as a stable, long-term user identifier — it changes every time a client reconnects. For scenarios like private messaging to a specific logged-in user, use Clients.User(userId) backed by a proper IUserIdProvider implementation instead of tracking raw connection IDs yourself.
- SignalR abstracts real-time, bidirectional push communication over WebSockets with automatic transport fallback.
- A Hub class exposes methods clients invoke, and can call back via Clients.Caller, Clients.All, Clients.Others, or Groups.
- Groups.AddToGroupAsync/RemoveFromGroupAsync manage named subsets of connections for targeted broadcasts.
- app.MapHub<T>("/route") registers a hub endpoint; JS/mobile clients connect via HubConnectionBuilder.
- Scaling across multiple server instances requires a backplane (commonly Redis) so broadcasts reach all connected clients.
- withAutomaticReconnect() handles reconnection, but group membership must be re-established manually after reconnect.
- Use Clients.User(userId) with an IUserIdProvider for stable per-user targeting instead of raw ConnectionId.
Practice what you learned
1. What is the primary advantage of SignalR over clients repeatedly polling an API endpoint?
2. Which transport does SignalR prefer when available, falling back to others when it is not?
3. How do you send a message to only the clients in a specific named subset of connections?
4. What is required to correctly broadcast messages when a SignalR app is scaled across multiple server instances?
5. Why is Context.ConnectionId unsuitable as a long-term identifier for a specific logged-in user?
Was this page helpful?
You May Also Like
The MVC Pattern in ASP.NET Core
Understand how ASP.NET Core implements Model-View-Controller, separating request handling, business/data concerns, and presentation into distinct, testable layers.
Blazor vs MVC vs Razor Pages
Compare ASP.NET Core's three UI programming models — Blazor, MVC, and Razor Pages — to understand their rendering models, state handling, and when each is the right choice.
Razor Pages Fundamentals
Learn the page-based programming model in ASP.NET Core where markup and code-behind logic live together, simplifying page-focused scenarios without a separate controller layer.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics