Llamada larga o canal eterno: cómo C# envía datos en tiempo real
\n\nImagina: estás escribiendo un chat en C#. O un terminal de bolsa. O un sistema de notificaciones. El cliente espera datos, y no está dispuesto a esperar mucho. Antes, los desarrolladores se las ingeniaban como podían: ponían temporizadores, consultaban el servidor cada 5 segundos, generaban solicitudes innecesarias. Hoy tenemos dos enfoques civilizados: Long Polling y WebSocket. Ambos permiten que el servidor envíe datos al cliente sin que este los solicite explícitamente. Pero funcionan de manera fundamentalmente diferente.
\n\nEn este artículo analizaremos cómo implementar ambos enfoques en C# (ASP.NET Core), los compararemos en velocidad, carga y complejidad, y entenderemos qué elegir para tu proyecto. ¡Vamos allá!
\n\n¿Qué es Long Polling? (Y por qué no es magia)
\n\nLong Polling es una emulación de tiempo real sobre HTTP normal. El cliente envía una solicitud al servidor, pero el servidor no responde de inmediato. Mantiene la conexión abierta hasta que ocurre un nuevo evento (o hasta que se agota el tiempo de espera). Tan pronto como los datos están listos, el servidor envía la respuesta, el cliente la procesa e inmediatamente abre una nueva solicitud "larga".
\n\nEs un truco ingenioso que funciona en cualquier servidor HTTP y en cualquier navegador. Pero tiene un costo: cada conexión permanece en la memoria del servidor, y si hay miles de clientes, eso ya es un problema.
\n\nEjemplo de Long Polling en C# (ASP.NET Core)
\n\nEscribamos un controlador simple que simule una cola de notificaciones. El cliente consulta el endpoint, el servidor espera nuevos datos (por ejemplo, de un canal Channel<string>) y los envía.
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 una notificación (por ejemplo, desde otro servicio)\n [HttpPost("publish")]\n public async Task<IActionResult> Publish([FromBody] string message)\n {\n await _channel.Writer.WriteAsync(message);\n return Ok("Notificación enviada");\n }\n\n // Long Polling: el cliente espera hasta que aparezca un nuevo mensaje\n [HttpGet("long-poll")]\n public async Task<IActionResult> LongPoll(CancellationToken cancellationToken)\n {\n // Esperamos un nuevo mensaje; si no hay nada, esperamos hasta 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 // Tiempo de espera agotado: devolvemos una respuesta vacía, el cliente repetirá la solicitud\n return Ok(new { data = (string?)null });\n }\n }\n}\n\n\nEl cliente (JavaScript) se vería así:
\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('Nueva notificación:', result.data);\n }\n // Inmediatamente iniciamos un nuevo poll\n }\n}\nstartLongPoll();\n\n\nTodo es simple. Pero hay un detalle: si el servidor se reinicia o el cliente pierde la conexión, los datos se pierden. Long Polling no garantiza la entrega.
\n\nWebSocket: túnel bidireccional en C#
\n\nWebSocket es un protocolo completo de comunicación bidireccional sobre TCP. Después del handshake (una solicitud HTTP GET normal con actualización), el cliente y el servidor se comunican directamente, sin la sobrecarga de los encabezados HTTP. El servidor puede enviar un mensaje en cualquier momento, y el cliente lo recibirá al instante.
\n\nEn ASP.NET Core, WebSocket está integrado de fábrica. No se necesitan bibliotecas de terceros (