Почему WebSockets — это магия, которую вы обязаны попробовать
Представьте: вы открыли сайт с курсом криптовалют. Цены прыгают каждую секунду. Или онлайн-чат, где сообщения приходят мгновенно, без «обновите страницу». Как это работает? Раньше браузер постоянно дёргал сервер: «Эй, есть что-то новое?» — а сервер отвечал: «Нет, иди спать». Это называется HTTP polling. Он медленный, жрёт трафик и нагружает сервер.
WebSockets — это как открытый канал связи. Сервер сам может сказать клиенту: «Смотри, обновление!» — без лишних запросов. В C# работать с WebSockets не просто удобно, а элегантно. Экосистема .NET даёт готовые инструменты: от нативных классов System.Net.WebSockets до мощного SignalR.
В этой статье мы не просто поговорим о теории. Мы напишем два реальных проекта: real-time чат и биржевой монитор. Вы увидите, как создать WebSocket сервер C# и подключить к нему клиента. Поехали!
Что такое WebSockets и почему C# — идеальный выбор
WebSocket — это протокол двусторонней связи поверх TCP. После рукопожатия (handshake) соединение остаётся открытым. Данные летят в обе стороны без заголовков HTTP. Это даёт:
- Скорость — задержка в миллисекундах.
- Экономию ресурсов — одно соединение вместо сотен запросов.
- Настоящий real-time — сервер сам инициирует отправку.
C# в этом контексте — настоящий тяжёлый артиллерист. ASP.NET Core поддерживает WebSockets из коробки. Плюс асинхронное программирование (async/await) идеально ложится на модель работы с каналами. Вы не блокируете поток, пока ждёте сообщение — это делает код производительным и читаемым.
Первый проект: real-time чат на WebSockets
Начнём с классики — чата. Он наглядно показывает всю механику: подключение, отправку, получение и закрытие соединения.
Шаг 1: Создаём WebSocket сервер на C#
Откройте Visual Studio или терминал. Создайте новый проект ASP.NET Core Web API (или пустой). Установите пакет, если нужно:
dotnet add package Microsoft.AspNetCore.WebSockets
Теперь в Program.cs настраиваем middleware. Вот минимальный код сервера:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWebSockets(options => { });
var app = builder.Build();
app.UseWebSockets();
// Список для хранения всех подключённых клиентов
var clients = new List<System.Net.WebSockets.WebSocket>();
app.Map("/chat", async (HttpContext context) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
clients.Add(webSocket);
Console.WriteLine("Новый клиент подключён!");
// Буфер для приёма данных
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,
"Соединение закрыто", CancellationToken.None);
clients.Remove(webSocket);
Console.WriteLine("Клиент отключён.");
}
else if (result.MessageType == WebSocketMessageType.Text)
{
// Получаем сообщение от одного клиента
var message = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Получено: {message}");
// Рассылаем всем остальным
var bytes = System.Text.Encoding.UTF8.GetBytes($"Сообщение: {message}");
foreach (var client in clients)
{
if (client.State == WebSocketState.Open)
{
await client.SendAsync(
new ArraySegment<byte>(bytes),
WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
}
}
else
{
context.Response.StatusCode = 400;
}
});
app.Run();
Важно: В реальном приложении используйте ConcurrentBag или синхронизацию, так как список не потокобезопасен. Но для демонстрации сойдёт.
Шаг 2: HTML-клиент для чата
Создайте простую HTML-страницу (или добавьте в папку wwwroot). Вот клиентский код:
<!DOCTYPE html>
<html>
<head>
<title>Real-Time Чат</title>
</head>
<body>
<h2>Чат на WebSockets</h2>
<input type="text" id="messageInput" placeholder="Введите сообщение" />
<button onclick="sendMessage()">Отправить</button>
<ul id="messages"></ul>
<script>
const socket = new WebSocket("ws://localhost:5000/chat");
socket.onopen = function() {
console.log("Подключено к серверу");
};
socket.onmessage = function(event) {
const li = document.createElement("li");
li.textContent = event.data;
document.getElementById("messages").appendChild(li);
};
socket.onclose = function() {
console.log("Соединение закрыто");
};
function sendMessage() {
const input = document.getElementById("messageInput");
socket.send(input.value);
input.value = "";
}
</script>
</body>
</html>
Запустите сервер (dotnet run), откройте страницу в двух вкладках и отправляйте сообщения. Магия происходит прямо сейчас — сообщения появляются мгновенно на всех клиентах.
Второй проект: биржевой монитор (сервер сам шлёт данные)
Теперь сделаем кое-что посложнее: сервер будет самостоятельно генерировать котировки и отправлять их всем клиентам. Это типичная задача для real-time уведомлений.
Шаг 3: WebSocket сервер с фоновой задачей
Добавим фоновый сервис, который каждые 2 секунды генерирует случайную цену и рассылает её. В том же проекте создайте класс PriceBroadcastService.cs:
public class PriceBroadcastService : BackgroundService
{
private readonly WebSocketConnectionManager _connectionManager;
public PriceBroadcastService(WebSocketConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var random = new Random();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(2000, stoppingToken);
var price = 100 + random.NextDouble() * 50; // Цена от 100 до 150
var message = $"{{ \"symbol\": \"BTC/USD\", \"price\": {price:F2} }}";
var bytes = System.Text.Encoding.UTF8.GetBytes(message);
// Отправляем всем активным клиентам
foreach (var socket in _connection