SignalR — Real-time Communication

লেসন ৪০ / ৫০

SignalR কী?

ASP.NET Core-এর real-time library — WebSocket / SSE / Long-polling-এর উপর abstraction। Chat, notifications, live dashboard।

Setup

// Program.cs
builder.Services.AddSignalR();

var app = builder.Build();
app.MapHub<ChatHub>("/chathub");

Hub — Server

public class ChatHub : Hub {
    public async Task SendMessage(string user, string msg) {
        await Clients.All.SendAsync("ReceiveMessage", user, msg);
    }

    public override async Task OnConnectedAsync() {
        await Clients.Caller.SendAsync("ReceiveMessage", "Server", "Welcome!");
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? e) {
        await Clients.Others.SendAsync("ReceiveMessage", "Server", $"User left");
        await base.OnDisconnectedAsync(e);
    }
}

Strongly-Typed Hub ⭐

public interface IChatClient {
    Task ReceiveMessage(string user, string msg);
    Task UserJoined(string user);
}

public class ChatHub : Hub<IChatClient> {
    public async Task SendMessage(string user, string msg) {
        await Clients.All.ReceiveMessage(user, msg);   // Type-safe
    }
}

Groups (Chat Rooms)

public class ChatHub : Hub<IChatClient> {
    public async Task JoinRoom(string room) {
        await Groups.AddToGroupAsync(Context.ConnectionId, room);
        await Clients.Group(room).UserJoined(Context.UserIdentifier!);
    }

    public async Task SendToRoom(string room, string msg) {
        await Clients.Group(room).ReceiveMessage(Context.UserIdentifier!, msg);
    }

    public async Task LeaveRoom(string room) {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, room);
    }
}

Send to Specific User

// User-based (requires auth)
await Clients.User(userId).ReceiveMessage("admin", "private msg");

// Connection-based
await Clients.Client(connectionId).ReceiveMessage(...);

// Except sender
await Clients.Others.ReceiveMessage(...);

From Controller

public class NotificationController : ControllerBase {
    private readonly IHubContext<ChatHub, IChatClient> _hub;
    public NotificationController(IHubContext<ChatHub, IChatClient> h) => _hub = h;

    [HttpPost("notify")]
    public async Task<IActionResult> Notify(string msg) {
        await _hub.Clients.All.ReceiveMessage("System", msg);
        return Ok();
    }
}

JavaScript Client

const conn = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

conn.on("ReceiveMessage", (user, msg) => {
    console.log(`${user}: ${msg}`);
});

await conn.start();
await conn.invoke("SendMessage", "Rahim", "Hello!");

Scale-out — Redis Backplane

builder.Services.AddSignalR().AddStackExchangeRedis("localhost:6379");
// একাধিক server থাকলেও সব client connected