WebSocket in C#: Creating Real-Time Applications in .NET
Modern web applications increasingly require instant data transfer between client and server. The traditional HTTP protocol, operating on a "request-response" scheme, is not always effective for tasks where update frequency and speed are important — chats, online games, stock quotes, monitoring systems. This is where the WebSocket protocol comes to the rescue, establishing a permanent bidirectional connection. In this article, we will thoroughly examine how to implement WebSocket in C# (.NET), create a full-fledged server and client, and also consider best practices.
What is WebSocket and why is it needed in .NET?
WebSocket is a protocol that works over TCP and provides full-duplex communication. After establishing a connection via an HTTP handshake, data can be transmitted in both directions without additional overhead. Unlike Long Polling or Server-Sent Events, WebSocket offers minimal latency and low traffic.
In the .NET ecosystem, there are two main approaches for working with real-time:
- Native WebSocket (System.Net.WebSockets) — a low-level API available in ASP.NET Core. It gives full control over the connection and is suitable for custom protocols.
- SignalR — a high-level library from Microsoft that abstracts WebSocket and automatically selects the transport (WebSocket, SSE, Long Polling). It is ideal for typical tasks (chats, notifications).
The choice depends on the task: if you need maximum control and performance — use native WebSocket; if development speed and simplicity are priorities — use SignalR.
Creating a WebSocket Server in C# (ASP.NET Core)
Let's look at a step-by-step server implementation. First, let's create an ASP.NET Core project (empty template or Web API).
Step 1: Middleware Configuration
Add WebSocket support in the Configure method of 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 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($"Received: {message}");
// Echo response var response = Encoding.UTF8.GetBytes($"Echo: {message}"); await webSocket.SendAsync( new ArraySegment<byte>(response), WebSocketMessageType.Text, true, CancellationToken.None); } }}This code creates an echo server: it accepts text messages and sends them back with the prefix "Echo:". Pay attention to connection state management and closure handling.
Step 2: Managing Multiple Connections
In a real application, you need to store a list of active connections. Use ConcurrentDictionary or ConcurrentBag:
public class WebSoc