Grupy, użytkownicy i zarządzanie połączeniami
Zarządzaj grupami, kieruj komunikaty do konkretnych użytkowników lub połączeń oraz obsługuj zdarzenia cyklu życia połączenia: connect/disconnect.
Grupy, użytkownicy i zarządzanie połączeniami to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.
Dlaczego grupy i użytkownicy
W rzeczywistych aplikacjach rzadko zachodzi potrzeba wysyłania wiadomości do WSZYSTKICH klientów. Grupy SignalR umożliwiają kierowanie wiadomości do podzbiorów połączeń (np. pokoju czatu), a użytkownicy pozwalają adresować wszystkie połączenia należące do konkretnego uwierzytelnionego użytkownika.
Dodawanie połączeń do grup
Wywołaj Groups.AddToGroupAsync(), podając identyfikator połączenia i nazwę grupy. Połączenie może jednocześnie należeć do wielu grup.
public class ChatHub : Hub
{
public async Task JoinRoom(string roomName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName)
.SendAsync("UserJoined", Context.User!.Identity!.Name);
}
public async Task LeaveRoom(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName)
.SendAsync("UserLeft", Context.User!.Identity!.Name);
}
}Wysyłanie do grup
Użyj Clients.Group(name), aby wysłać wiadomość do wszystkich połączeń w grupie. Inne opcje kierowania obejmują GroupExcept oraz Groups (wiele grup naraz).
public async Task SendToRoom(string roomName, string message)
{
var sender = Context.User!.Identity!.Name;
// Everyone in the room
await Clients.Group(roomName)
.SendAsync("ReceiveMessage", sender, message);
// Everyone in the room except the sender
await Clients.GroupExcept(roomName, Context.ConnectionId)
.SendAsync("ReceiveMessage", sender, message);
}Kierowanie według użytkownika
Gdy użytkownik ma otwartych wiele kart przeglądarki lub urządzeń, ma wiele połączeń. Clients.User(userId) wysyła wiadomość jednocześnie do WSZYSTKICH połączeń tego użytkownika.
// Send to all connections of user with ID "user-123"
await Clients.User("user-123")
.SendAsync("Notification", "Your order has shipped!");
// SignalR uses the NameIdentifier claim by default.
// Context.UserIdentifier returns the current user's ID.
var myId = Context.UserIdentifier;
await Clients.User(myId!).SendAsync("Ack", "Server received your message");Niestandardowy dostawca identyfikatora użytkownika
Domyślnie SignalR używa claimu NameIdentifier jako identyfikatora użytkownika. Zaimplementuj IUserIdProvider, aby używać innego claimu lub niestandardowej logiki.
public class EmailUserIdProvider : IUserIdProvider
{
public string? GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
// Register:
builder.Services.AddSingleton<IUserIdProvider, EmailUserIdProvider>();Śledzenie stanu połączeń
SignalR nie utrwala stanu połączenia między rozłączeniami. Należy śledzić własne mapowanie identyfikatorów użytkowników na identyfikatory połączeń w bezpiecznym wątkowo słowniku (jeden serwer) lub Redisie (wiele serwerów).
public class ConnectionTracker
{
private readonly ConcurrentDictionary<string, HashSet<string>> _userConnections
= new();
public void AddConnection(string userId, string connectionId)
=> _userConnections.GetOrAdd(userId, _ => new()).Add(connectionId);
public void RemoveConnection(string userId, string connectionId)
{
if (_userConnections.TryGetValue(userId, out var conns))
{
conns.Remove(connectionId);
if (conns.Count == 0) _userConnections.TryRemove(userId, out _);
}
}
public IEnumerable<string> GetConnections(string userId)
=> _userConnections.TryGetValue(userId, out var c) ? c : Enumerable.Empty<string>();
}Automatyczne zarządzanie grupami w OnConnected/OnDisconnected
Częsty wzorzec polega na automatycznym dodawaniu uwierzytelnionych użytkowników do ich osobistej grupy w OnConnectedAsync, aby można było kierować do nich wiadomości według nazwy grupy bez niestandardowego śledzenia.
public override async Task OnConnectedAsync()
{
// Add to a personal group named by user ID
var userId = Context.UserIdentifier;
if (userId is not null)
await Groups.AddToGroupAsync(Context.ConnectionId, userId);
await base.OnConnectedAsync();
}
// Now send to a user by their personal group:
await _hub.Clients.Group(userId)
.SendAsync("Update", payload);Wysyłanie z usług działających w tle za pomocą grup
Korzystając z IHubContext, można kierować wiadomości do grup i użytkowników z usług działających w tle — idealne rozwiązanie dla asynchronicznych powiadomień wyzwalanych przez zdarzenia w systemie.
public class OrderShippedHandler : INotificationHandler<OrderShipped>
{
private readonly IHubContext<OrderHub> _hub;
public OrderShippedHandler(IHubContext<OrderHub> hub) => _hub = hub;
public async Task Handle(OrderShipped notification, CancellationToken ct)
{
// Push to the customer's personal group
await _hub.Clients.Group(notification.CustomerId.ToString())
.SendAsync("OrderShipped",
new { notification.OrderId, notification.TrackingNumber },
ct);
}
}Utrwalanie członkostwa w grupach
Członkostwo w grupach nie jest utrwalane — po ponownym uruchomieniu serwera lub utracie stanu przez backplane Redis grupy są puste. Po ponownym połączeniu należy ponownie dołączyć do grup, korzystając ze zdarzenia klienta onreconnected.
// JavaScript client: re-join groups after reconnect
connection.onreconnected(async connectionId => {
console.log(`Reconnected as ${connectionId}`);
// Re-join previously joined rooms
for (const room of joinedRooms) {
await connection.invoke("JoinRoom", room);
}
});Obecność: wykrywanie statusu online/offline
Status online można śledzić, zliczając połączenia każdego użytkownika w OnConnected/OnDisconnected i rozgłaszając zdarzenia obecności do odpowiednich grup.
public override async Task OnConnectedAsync()
{
var userId = Context.UserIdentifier!;
var count = _tracker.AddConnection(userId, Context.ConnectionId);
if (count == 1) // first connection for this user
await Clients.Others.SendAsync("UserOnline", userId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? ex)
{
var userId = Context.UserIdentifier!;
var count = _tracker.RemoveConnection(userId, Context.ConnectionId);
if (count == 0) // no more connections
await Clients.Others.SendAsync("UserOffline", userId);
await base.OnDisconnectedAsync(ex);
}Szybkie sprawdzenie
Co robi Clients.User(userId), gdy użytkownik ma otwarte trzy karty przeglądarki?
Podsumowanie: zarządzanie grupami, użytkownikami i połączeniami
Najważniejsze informacje:
Groups.AddToGroupAsync/RemoveFromGroupAsync: zarządzanie członkostwem w grupachClients.Group(name): wysyłanie wiadomości do wybranego podzbioru połączeńClients.User(userId): wysyłanie wiadomości do wszystkich połączeń użytkownika- Należy zaimplementować
IUserIdProviderw celu obsługi niestandardowych deklaracji identyfikatora użytkownika - Grupy nie są utrwalane — po ponownym połączeniu należy do nich ponownie dołączyć
- Liczbę połączeń należy śledzić w ConcurrentDictionary w celu wykrywania obecności
Często zadawane pytania
Czy lekcja „Grupy, użytkownicy i zarządzanie połączeniami” jest bezpłatna?
Tak — pełny tekst „Grupy, użytkownicy i zarządzanie połączeniami” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Grupy, użytkownicy i zarządzanie połączeniami”?
Zarządzaj grupami, kieruj komunikaty do konkretnych użytkowników lub połączeń oraz obsługuj zdarzenia cyklu życia połączenia: connect/disconnect. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Grupy, użytkownicy i zarządzanie połączeniami”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Huby i połączenia SignalR
- Grupy, użytkownicy i zarządzanie połączeniami
- Silnie typowane huby
- Skalowanie z Redis Backplane