Introdução: Por que WebSocket em C# é a escolha dos profissionais
No desenvolvimento web moderno, a interação em tempo real se tornou padrão. Plataformas de negociação, sistemas de monitoramento, jogos online e chats — todos exigem transmissão instantânea de dados. A tecnologia WebSocket em C# (.NET) fornece uma ferramenta poderosa e flexível para criar tais aplicações. Ao contrário das consultas HTTP, o WebSocket estabelece uma conexão bidirecional permanente, o que reduz radicalmente a latência e a carga no servidor.
Neste artigo, exploraremos aspectos avançados do trabalho com WebSocket em C#. Você aprenderá como construir um servidor tolerante a falhas, gerenciar milhares de conexões simultâneas, tratar erros e integrar WebSocket com frameworks populares. O material é voltado para desenvolvedores já familiarizados com os fundamentos do .NET e programação assíncrona.
Arquitetura de um servidor WebSocket avançado em C#
O exemplo básico de um servidor WebSocket em C# geralmente se limita a um único manipulador. Para soluções de produção, é necessária uma arquitetura bem planejada. Vamos considerar os componentes-chave.
Gerenciamento de conexões e identificação de clientes
Em vez de uma simples lista de conexões, use ConcurrentDictionary para armazenamento thread-safe. Cada cliente recebe um identificador único (GUID) e metadados são armazenados (por exemplo, nome de usuário, grupo).
public class WebSocketConnectionManager
{
private readonly ConcurrentDictionary<string, WebSocket> _connections = new();
private readonly ConcurrentDictionary<string, UserMetadata> _users = new();
public string AddConnection(WebSocket socket, UserMetadata metadata)
{
var id = Guid.NewGuid().ToString();
_connections.TryAdd(id, socket);
_users.TryAdd(id, metadata);
return id;
}
public async Task RemoveConnection(string id)
{
if (_connections.TryRemove(id, out var socket) && socket.State == WebSocketState.Open)
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed", CancellationToken.None);
}
_users.TryRemove(id, out _);
}
public WebSocket? GetSocketById(string id) => _connections.GetValueOrDefault(id);
}
Essa abordagem permite enviar facilmente mensagens para usuários específicos, grupos ou todos de uma vez.
Tratamento de mensagens fragmentadas
Em aplicações reais, os dados podem chegar em fragmentos. O método padrão ReceiveAsync retorna um fragmento por vez. Para montar a mensagem completa, use um loop:
public async Task<byte[]> ReceiveFullMessageAsync(WebSocket socket, CancellationToken ct)
{
using var ms = new MemoryStream();
WebSocketReceiveResult result;
var buffer = new byte[1024 * 4]; // Buffer de 4KB
do
{
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), ct);
ms.Write(buffer, 0, result.Count);
} while (!result.EndOfMessage);
return ms.ToArray();
}
Isso é crítico para transmitir grandes objetos JSON ou dados binários (imagens, arquivos).
Escalabilidade: SignalR vs Raw WebSocket
Para aplicações complexas, surge a questão de escolher entre WebSocket de baixo nível e SignalR — uma biblioteca da Microsoft que abstrai o WebSocket. Vamos considerar os cenários.
Quando usar Raw WebSocket
- Máxima performance e mínimo overhead.
- Protocolo de transmissão próprio (dados binários, frames personalizados).
- Integração com clientes não-.NET (por exemplo, Go ou Rust).
- Controle total sobre o gerenciamento de conexões.
Quando escolher SignalR
- Escalabilidade automática via Redis Backplane ou Azure SignalR Service.
- Suporte a transportes alternativos (Long Polling, Server-Sent Events).
- Modelo de hubs integrado para broadcasting e grupos.
- Reconexão automática e gerenciamento de estado.