WebSocket en C#: creación de aplicaciones en tiempo real en .NET
Las aplicaciones web modernas requieren cada vez más la transmisión instantánea de datos entre el cliente y el servidor. El protocolo HTTP tradicional, que funciona bajo el esquema de "solicitud-respuesta", no siempre es eficiente para tareas donde la frecuencia y velocidad de las actualizaciones son importantes — chats, juegos en línea, cotizaciones bursátiles, sistemas de monitoreo. Aquí es donde entra en juego el protocolo WebSocket, que establece una conexión bidireccional permanente. En este artículo, analizaremos en detalle cómo implementar WebSocket en C# (.NET), crearemos un servidor y cliente completos, y también revisaremos las mejores prácticas.
¿Qué es WebSocket y por qué es necesario en .NET?
WebSocket es un protocolo que funciona sobre TCP y proporciona comunicación full-duplex. Después de establecer la conexión mediante un handshake HTTP, los datos pueden transmitirse en ambas direcciones sin gastos generales adicionales. A diferencia de Long Polling o Server-Sent Events, WebSocket ofrece latencia mínima y bajo tráfico.
En el ecosistema .NET hay dos enfoques principales para trabajar con tiempo real:
- WebSocket nativo (System.Net.WebSockets) — API de bajo nivel disponible en ASP.NET Core. Proporciona control total sobre la conexión, adecuado para protocolos personalizados.
- SignalR — biblioteca de alto nivel de Microsoft que abstrae WebSocket y selecciona automáticamente el transporte (WebSocket, SSE, Long Polling). Ideal para tareas típicas (chats, notificaciones).
La elección depende de la tarea: si necesita máximo control y rendimiento — use WebSocket nativo; si prioriza velocidad de desarrollo y simplicidad — SignalR.
Creación de un servidor WebSocket en C# (ASP.NET Core)
Veamos una implementación paso a paso del servidor. Primero, crearemos un proyecto ASP.NET Core (plantilla vacía o Web API).
Paso 1: Configuración del Middleware
Agregue soporte para WebSocket en el método Configure del archivo Program.cs:
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.UseWebSockets(); // Habilitamos soporte para WebSocket
app.Map("/ws", async (HttpContext context) =>{ if (context.WebSockets.IsWebSocketRequest) { using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); await HandleWebSocketConnection(webSocket); } else { context.Response.StatusCode = 400; }});
app.Run();
async Task HandleWebSocketConnection(WebSocket webSocket){ var buffer = new byte[1024 * 4]; while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync( new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close) { await webSocket.CloseAsync( WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); } else if (result.MessageType == WebSocketMessageType.Text) { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); Console.WriteLine($"Recibido: {message}");
// Respuesta eco var response = Encoding.UTF8.GetBytes($"Eco: {message}"); await webSocket.SendAsync( new ArraySegment<byte>(response), WebSocketMessageType.Text, true, CancellationToken.None); } }}Este código crea un servidor eco: recibe mensajes de texto y los envía de vuelta con el prefijo "Eco:". Observe la gestión del estado de la conexión y el manejo del cierre.
Paso 2: Gestión de múltiples conexiones
En una aplicación real, es necesario almacenar una lista de conexiones activas. Use ConcurrentDictionary o ConcurrentBag:
public class WebSoc