Skip to content

SignalR

Messaging & StreamingNetworking / WebSocketC#

What it is

SignalR adds real-time bidirectional communication to ASP.NET Core, negotiating WebSockets where available and falling back automatically.

Define a Hub with methods clients can call, and push to clients through strongly-typed interfaces. Groups let you address subsets of connections.

Installation

Included in ASP.NET Core

Getting started

The smallest useful thing you can do with it, and what each part means.

A typed hub
public interface IBookClient
{
    Task BookAdded(BookDto book);
    Task BookRemoved(int id);
}

public class BookHub : Hub<IBookClient>
{
    public async Task JoinLibrary(string libraryId) =>
        await Groups.AddToGroupAsync(Context.ConnectionId, libraryId);

    public override async Task OnDisconnectedAsync(Exception? ex)
    {
        await base.OnDisconnectedAsync(ex);
    }
}

app.MapHub<BookHub>("/hubs/books");

// Push from anywhere via the injected context.
await _hub.Clients.Group(libraryId).BookAdded(dto);
Hub<T> gives compile-time checking of client method names. Untyped Clients.All.SendAsync("BookAdded", …) uses a magic string that breaks silently when renamed.

Advanced usage

Where the library earns its place over a simpler alternative.

Scaling out and client reconnection
// Multiple servers need a backplane, or a message published on
// instance A never reaches a client connected to instance B.
builder.Services.AddSignalR()
    .AddStackExchangeRedis(redisConnectionString);

// Client
const connection = new signalR.HubConnectionBuilder()
  .withUrl('/hubs/books')
  .withAutomaticReconnect([0, 2000, 10000, 30000])
  .build();

connection.on('BookAdded', (book) => render(book));
connection.onreconnected(() => connection.invoke('JoinLibrary', libraryId));
Two things people miss: without a backplane SignalR silently breaks the moment you scale past one instance, and group membership is lost on reconnect, so it must be re-established.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Messages reach only some clients
Multiple server instances with no backplane. Add Redis or Azure SignalR Service.
Clients stop receiving after a network blip
They reconnected but did not rejoin their groups. Handle onreconnected.

Best practices

  • Use Hub<TClient> for compile-time checked client method names.
  • Add a Redis backplane before scaling beyond a single instance.
  • Re-join groups in the client's onreconnected handler; membership does not survive a reconnect.
  • Authorise hub methods; a hub is a public endpoint like any controller.

Background

Why it exists, and what it was reacting to.

SignalR abstracts over the transport problem: it prefers WebSockets, falls back to server-sent events or long polling, and handles reconnection, so applications do not implement three code paths.