100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

SignalR for Real-Time Communication

Learn how ASP.NET Core SignalR enables real-time, bidirectional communication between server and clients using WebSockets and automatic transport fallback.

Razor Pages & MVCIntermediate10 min readJul 10, 2026
Analogies

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.

csharp
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

Was this page helpful?

Topics covered

#ASPNETCoreStudyNotes#MicrosoftTechnologies#SignalRForRealTimeCommunication#SignalR#Real#Time#Communication#StudyNotes#SkillVeris#ExamPrep