Chamada longa ou canal eterno: como o C# entrega dados em tempo real
\n\nImagine: você está escrevendo um chat em C#. Ou um terminal de ações. Ou um sistema de notificações. O cliente espera por dados — e não está disposto a esperar muito. Antigamente, os desenvolvedores se viravam como podiam: colocavam timers, faziam requisições ao servidor a cada 5 segundos, geravam requisições desnecessárias. Hoje, temos duas abordagens civilizadas — Long Polling e WebSocket. Ambas permitem que o servidor envie dados ao cliente sem uma requisição explícita dele. Mas funcionam de maneiras fundamentalmente diferentes.
\n\nNeste artigo, vamos analisar como implementar ambas as abordagens em C# (ASP.NET Core), compará-las em termos de velocidade, carga e complexidade, e entender o que escolher para o seu projeto. Vamos nessa.
\n\nO que é Long Polling? (E por que não é mágica)
\n\nLong Polling é uma emulação de tempo real sobre HTTP comum. O cliente envia uma requisição ao servidor, mas o servidor não responde imediatamente. Ele mantém a conexão aberta até que um novo evento ocorra (ou até que o tempo limite expire). Assim que os dados estão prontos — o servidor envia a resposta, o cliente a processa e imediatamente abre uma nova requisição "longa".
\n\nEste é um truque esperto que funciona em qualquer servidor HTTP e em qualquer navegador. Mas tem um custo: cada conexão fica na memória do servidor e, se houver milhares de clientes, isso já é um problema.
\n\nExemplo de Long Polling em C# (ASP.NET Core)
\n\nVamos escrever um controlador simples que simula uma fila de notificações. O cliente acessa o endpoint, o servidor espera por novos dados (por exemplo, de um canal Channel<string>) e os envia.
using Microsoft.AspNetCore.Mvc;\nusing System.Threading.Channels;\n\n[ApiController]\n[Route("api/[controller]")]\npublic class NotificationsController : ControllerBase\n{\n private static readonly Channel<string> _channel = Channel.CreateUnbounded<string>();\n\n // Endpoint para publicar uma notificação (por exemplo, de outro serviço)\n [HttpPost("publish")]\n public async Task<IActionResult> Publish([FromBody] string message)\n {\n await _channel.Writer.WriteAsync(message);\n return Ok("Notificação enviada");\n }\n\n // Long Polling: o cliente espera até que uma nova mensagem apareça\n [HttpGet("long-poll")]\n public async Task<IActionResult> LongPoll(CancellationToken cancellationToken)\n {\n // Aguarda uma nova mensagem; se não houver, espera até 30 segundos\n using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n cts.CancelAfter(TimeSpan.FromSeconds(30));\n\n try\n {\n var message = await _channel.Reader.ReadAsync(cts.Token);\n return Ok(new { data = message });\n }\n catch (OperationCanceledException)\n {\n // Tempo limite — retorna resposta vazia, o cliente repetirá a requisição\n return Ok(new { data = (string?)null });\n }\n }\n}\n\n\nO cliente (JavaScript) ficará assim:
\n\nasync function startLongPoll() {\n while (true) {\n const response = await fetch('/api/notifications/long-poll');\n const result = await response.json();\n if (result.data) {\n console.log('Nova notificação:', result.data);\n }\n // Imediatamente inicia um novo poll\n }\n}\nstartLongPoll();\n\n\nTudo simples. Mas há um detalhe: se o servidor reiniciar ou o cliente perder a conexão — os dados serão perdidos. O Long Polling não garante a entrega.
\n\nWebSocket: túnel bidirecional em C#
\n\nWebSocket é um protocolo completo de comunicação bidirecional sobre TCP. Após o handshake (um HTTP GET comum com upgrade), o cliente e o servidor se comunicam diretamente, sem a sobrecarga dos cabeçalhos HTTP. O servidor pode enviar uma mensagem a qualquer momento, e o cliente a receberá instantaneamente.
\n\nNo ASP.NET Core, o WebSocket é integrado nativamente. Não são necessárias bibliotecas de terceiros (