Introducción: ¿Por qué WebSocket es el estándar para aplicaciones en tiempo real?
Las aplicaciones web modernas requieren una reacción instantánea a las acciones del usuario. Ya sea un chat en línea, un terminal bursátil, un juego multijugador o un sistema de monitoreo, los retrasos en la transmisión de datos son inaceptables. El protocolo HTTP tradicional, que funciona bajo el principio de "solicitud-respuesta", es ineficiente para estos escenarios: el cliente tiene que consultar constantemente al servidor (polling), creando una carga redundante.
En su ayuda viene WebSocket, un protocolo que proporciona una conexión bidireccional permanente entre el cliente y el servidor. En este artículo, analizaremos cómo crear aplicaciones en tiempo real en C# (.NET) usando WebSocket, desde un simple servidor eco hasta un chat completo. Aprenderá cómo funciona el protocolo, qué herramientas ofrece la plataforma .NET y cómo evitar errores típicos.
Fundamentos de WebSocket en .NET: Parte del servidor
La plataforma .NET ofrece soporte integrado para WebSocket a través del espacio de nombres System.Net.WebSockets. Veamos la creación de un servidor basado en ASP.NET Core.
1. Configuración de Middleware para WebSocket
Para empezar, es necesario agregar soporte para WebSocket en el pipeline de procesamiento de solicitudes. En el archivo Program.cs:
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.UseWebSockets(); // Habilitamos el soporte para WebSocket
app.Map("/ws", async (HttpContext context) =>{ if (context.WebSockets.IsWebSocketRequest) { using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); await Echo(webSocket); } else { context.Response.StatusCode = 400; }});
app.Run("http://localhost:5000");
async Task Echo(WebSocket webSocket){ var buffer = new byte[1024 * 4]; var receiveResult = await webSocket.ReceiveAsync( new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue) { // Enviamos los datos recibidos de vuelta al cliente (eco) await webSocket.SendAsync( new ArraySegment<byte>(buffer, 0, receiveResult.Count), receiveResult.MessageType, receiveResult.EndOfMessage, CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync( new ArraySegment<byte>(buffer), CancellationToken.None); }
await webSocket.CloseAsync( receiveResult.CloseStatus.Value, receiveResult.CloseStatusDescription, CancellationToken.None);}Este código crea un servidor eco: todo lo que el cliente envía, se devuelve. Observe el uso de async/await — el trabajo con WebSocket siempre es asíncrono.
2. Gestión de múltiples conexiones
En una aplicación real, es necesario gestionar múltiples conexiones. Creemos un simple gestor de conexiones:
public class WebSocketConnectionManager{ private readonly ConcurrentDictionary<string, WebSocket> _connections = new();
public string AddConnection(WebSocket socket) { var id = Guid.NewGuid().ToString(); _connections.TryAdd(id, socket); return id; }
public async Task BroadcastMessageAsync(string message) { var tasks = _connections.Values.Select(async socket => { if (socket.State == WebSocketState.Open) { var bytes = Encoding.UTF8.GetBytes(message); await socket.SendAsync( new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None); } }); await Task.WhenAll(tasks); }}Esta clase permite almacenar todas las conexiones activas y difundir mensajes