Skip to main content

Introdução | WebSocket | SignalR | CSharp

  • Introdução | WebSocket | SignalR | CSharp

alt text

info

WebSocket é um upgrade da conexão HTTP, mas não exatamente uma "versão melhorada" — é uma mudança de protocolo feita após a negociação HTTP inicial.

1- Cliente faz uma request http:

GET /algum-endpoint HTTP/1.1
Host: exemplo.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxxxxxx==
Sec-WebSocket-Version: 13

2- O servidor se suportar websocket retorna:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: yyyyyyyyy=
danger

Cuidado nos métodos que assinam o evento, o valor precisa ser um object

 connection.On<string>("ReceberMensagem", mensagem =>
{
Console.WriteLine($"Mensagem recebida: {mensagem}");
});

Hub

  • É um ponto central de conexão no servidor.
  • Para criar um hub, a classe deve herdar de Microsoft.AspNetCore.SignalR.Hub

Escopo do Hub (IMPORTANTE)

Cada Hub é isolado em termos de conexão.

ConceitoComportamento
ConexõesSão específicas por Hub
UsersSão resolvidos logicamente, mas conexões são por Hub
GroupsExistem apenas dentro do Hub
ClientsSó enxergam conexões daquele Hub

Regra prática

  • Um usuário pode estar conectado em vários Hubs ao mesmo tempo
  • Cada conexão gera um ConnectionId diferente
  • Um Hub não acessa diretamente conexões de outro Hub

Exemplo:

User "joao" conectado em:
- ChatHub → ConnectionId A
- NotificationHub → ConnectionId B
  • ChatHub NÃO enxerga ConnectionId B
  • NotificationHub NÃO enxerga ConnectionId A

📌 Conclusão arquitetural:

Hub = boundary de conexão (isolamento de transporte)


Clientes

alt text

info

Client é a conexão individual com o SignalR (ConnectionId), enquanto User é a identidade lógica que agrupa múltiplas conexões do mesmo usuário.

  • Ao chamar um hub sendo cliente, há duas formas:
MétodoComportamento
connection.invokeEspera retorno (RPC-like)
connection.sendFire-and-forget

Servidor

Como escalar o servidor de WebSocket?


Clients (envio de mensagens - broadcast, unicast, multicast)

MétodoTipo de envioEscopoInclui Caller?Observações arquiteturais
Clients.AllBroadcast (um → todos)Todos no Hub✅ SimEnvia para todas as conexões do Hub
Clients.CallerUnicast (um → um)Conexão atual✅ SimUsado como resposta (RPC-like)
Clients.OthersBroadcast (um → todos - caller)Todos no Hub❌ NãoEvita eco para quem enviou
Clients.AllExcept(ids)Broadcast seletivo (um → muitos)Todos exceto listaDependeControle fino por ConnectionId
Clients.Client(id)Unicast (um → um específico)Conexão específica❌ NãoEndereçamento físico (baixo nível)
Clients.Clients(ids)Multicast (um → muitos específicos)Lista de conexões❌ NãoBatch de conexões específicas
Clients.User(userId)Unicast lógico (um → usuário)Usuário❌ NãoEnvia para todas conexões do usuário neste Hub
Clients.Users(userIds)Multicast lógico (um → vários usuários)Usuários❌ NãoMúltiplos usuários
Clients.Group(name)Multicast (um → grupo)Grupo❌ NãoGrupo é isolado por Hub
Clients.Groups(names)Multicast (um → vários grupos)Vários grupos❌ NãoFan-out para múltiplos grupos
Clients.GroupExcept(name, ids)Multicast seletivo (grupo → muitos)Grupo❌ NãoGrupo menos algumas conexões
Clients.OthersInGroup(name)Multicast (grupo → todos - caller)Grupo❌ NãoGrupo sem o caller

Observações críticas

Tudo é por Hub

Mesmo User:

User "joao"
├── ChatHub → recebe
└── NotificationHub → NÃO recebe

Clients.User NÃO é global entre hubs


Hierarquia de endereçamento

ConnectionId (nível físico)

User (agrupa conexões)

Group (agrupamento dinâmico)

Diferença entre conceitos

ConceitoPersistênciaEscopoUso
ConnectionHubControle técnico
User✅ (auth)HubIdentidade
GroupHubSegmentação dinâmica

Quando usar cada um

CenárioMétodo
Broadcast geralAll
Resposta diretaCaller
Broadcast sem ecoOthers
Notificação por usuárioUser
Sala (chat room)Group
Target específicoClient

Anatomia de uma conexão

  • ConnectionId → Identificador único
  • UserIdentifier → ID lógico do usuário
  • User → ClaimsPrincipal
  • Items → Metadados da conexão
  • ConnectionAborted → Cancelamento da conexão

📌 Importante:

  • ConnectionId muda a cada reconexão
  • Não usar ConnectionId como identidade de negócio

Negotiate

A rota negotiate é parte fundamental do processo de conexão no SignalR.

Propósito

  1. Negociação de protocolo (versão, formato)

  2. Seleção de transporte:

    • WebSocket
    • Server-Sent Events
    • Long Polling
  3. Geração de token de conexão


Fluxo

1. Cliente → GET /hub/negotiate
2. Servidor → retorna JSON:
- connectionId
- availableTransports
- url/token

3. Cliente → inicia transporte (WebSocket, etc)

Exemplo de resposta

{
"connectionId": "abc123",
"availableTransports": [
{ "transport": "WebSockets" },
{ "transport": "ServerSentEvents" },
{ "transport": "LongPolling" }
]
}

Observação arquitetural

  • O negotiate desacopla:

    • cliente vs transporte
    • protocolo vs infraestrutura

👉 Permite fallback automático


Segurança

  • Deve exigir autenticação
  • Tokens devem ser temporários
  • Evita spoofing de conexão

Resumo mental

SignalR

Hub
├── Connections (isoladas por hub)
├── Users (lógico, mas escopo do hub)
└── Groups (isolados)

Negotiate
└── Define como a conexão será criada