Introduction: Why WebSocket is the Standard for Real-time Applications?
Modern web applications require instant response to user actions. Whether it's an online chat, a trading terminal, a multiplayer game, or a monitoring system — delays in data transmission are unacceptable. The traditional HTTP protocol, operating on a "request-response" principle, is inefficient for such scenarios: the client has to constantly poll the server, creating redundant load.
WebSocket comes to the rescue — a protocol that provides a persistent two-way connection between the client and server. In this article, we will explore how to create real-time applications in C# (.NET) using WebSocket, from a simple echo server to a full-fledged chat. You will learn how the protocol works, what tools the .NET platform offers, and how to avoid common mistakes.
WebSocket Basics in .NET: Server Side
The .NET platform offers built-in WebSocket support through the System.Net.WebSockets namespace. Let's look at creating a server based on ASP.NET Core.
1. Configuring WebSocket Middleware
First, you need to add WebSocket support to the request processing pipeline. In the Program.cs file:
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.UseWebSockets(); // Enable WebSocket support
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) { // Send received data back to the client (echo) 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);}This code creates an echo server: everything the client sends is returned back. Note the use of async/await — working with WebSocket is always asynchronous.
2. Managing Multiple Connections
In a real application, you need to manage multiple connections. Let's create a simple connection manager:
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); }}This class allows you to store all active connections and broadcast messages