Introducción a WebSocket en C# para aplicaciones en tiempo real
Las aplicaciones web modernas requieren cada vez más una 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 es eficiente para tareas donde son importantes una baja latencia y una actualización constante de la información: chats en línea, cotizaciones bursátiles, juegos multijugador, sistemas de monitoreo. Aquí es donde entra en juego WebSocket, un protocolo que proporciona una conexión bidireccional permanente.
En el ecosistema de C# (.NET), la implementación de WebSocket está disponible tanto a través de las clases integradas del espacio de nombres System.Net.WebSockets como a través de la biblioteca de alto nivel SignalR. En este artículo, analizaremos ambos enfoques, escribiremos ejemplos funcionales y discutiremos las características arquitectónicas de las aplicaciones en tiempo real. El material será útil tanto para desarrolladores .NET principiantes como para los experimentados.
Fundamentos de WebSocket: cómo funciona en C#
WebSocket es un protocolo que, después del handshake HTTP inicial (solicitud de actualización), transforma la conexión en un modo bidireccional permanente. En C#, el trabajo con WebSocket se basa en la programación asíncrona utilizando las palabras clave async y await.
Creación de un servidor WebSocket simple en C#
Consideremos un ejemplo básico de un servidor que acepta conexiones WebSocket y responde al cliente con eco. Para ello, utilizaremos la clase integrada HttpListener o Kestrel (ASP.NET Core). A continuación se muestra un ejemplo basado en ASP.NET Core.
// Startup.cs o Program.cs (API mínima)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWebSockets();
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;
}
});
async Task Echo(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
{
// Enviamos los datos de vuelta (eco)
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, result.Count),
result.MessageType,
result.EndOfMessage,
CancellationToken.None);
}
}
}
app.Run();
El código demuestra la mecánica básica: AcceptWebSocketAsync transforma la solicitud HTTP en WebSocket, y los métodos ReceiveAsync y SendAsync proporcionan el intercambio de datos asíncrono. Es importante manejar el estado de la conexión y cerrarla correctamente.
Cliente WebSocket en C#
La parte del cliente también es simple. Usamos la clase ClientWebSocket:
using System.Net.WebSockets;
async Task ConnectAndSend()
{
using var client = new ClientWebSocket();
await client.ConnectAsync(new Uri("ws://localhost:5000/ws"), CancellationToken.None);
var message = "¡Hola, WebSocket!";
var bytes = Encoding.UTF8.GetBytes(message);
await client.SendAsync(new ArraySegment<byte>(bytes),
WebSocketMessageType.Text, true, CancellationToken.None);
var buffer = new byte[1024];
var result = await client.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
var response = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Respuesta del servidor: {response}");
}
```