WebSockets в C#: Создаём real-time приложения, которые работают как часы

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему 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                

Блоги

Рекомендаций по книгам