WebSocket на C#: создание real-time приложений в .NET

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

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

Начать курс

WebSocket на C#: создание real-time приложений в .NET

Современные веб-приложения всё чаще требуют мгновенной передачи данных между клиентом и сервером. Традиционный HTTP-протокол, работающий по схеме «запрос-ответ», не всегда эффективен для задач, где важна частота и скорость обновлений — чаты, онлайн-игры, биржевые котировки, системы мониторинга. Здесь на помощь приходит протокол WebSocket, который устанавливает постоянное двунаправленное соединение. В этой статье мы подробно разберём, как реализовать WebSocket на C# (.NET), создадим полноценный сервер и клиент, а также рассмотрим лучшие практики.

Что такое WebSocket и зачем он нужен в .NET?

WebSocket — это протокол, работающий поверх TCP, который обеспечивает полнодуплексную связь. После установки соединения через HTTP-рукопожатие (handshake), данные могут передаваться в обе стороны без дополнительных накладных расходов. В отличие от Long Polling или Server-Sent Events, WebSocket даёт минимальную задержку и низкий трафик.

В экосистеме .NET есть два основных подхода для работы с real-time:

  • Нативный WebSocket (System.Net.WebSockets) — низкоуровневый API, доступный в ASP.NET Core. Даёт полный контроль над соединением, подходит для кастомных протоколов.
  • SignalR — высокоуровневая библиотека от Microsoft, которая абстрагирует WebSocket и автоматически выбирает транспорт (WebSocket, SSE, Long Polling). Идеальна для типовых задач (чаты, уведомления).

Выбор зависит от задачи: если вам нужен максимальный контроль и производительность — используйте нативный WebSocket; если скорость разработки и простота — SignalR.

Создание WebSocket сервера на C# (ASP.NET Core)

Рассмотрим пошаговую реализацию сервера. Для начала создадим проект ASP.NET Core (пустой шаблон или Web API).

Шаг 1: Настройка Middleware

Добавьте поддержку WebSocket в методе Configure файла Program.cs:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseWebSockets(); // Включаем поддержку WebSocket

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($"Получено: {message}");

            // Эхо-ответ
            var response = Encoding.UTF8.GetBytes($"Эхо: {message}");
            await webSocket.SendAsync(
                new ArraySegment<byte>(response), 
                WebSocketMessageType.Text, 
                true, 
                CancellationToken.None);
        }
    }
}

Этот код создаёт эхо-сервер: он принимает текстовые сообщения и отправляет их обратно с префиксом "Эхо:". Обратите внимание на управление состоянием соединения и обработку закрытия.

Шаг 2: Управление множественными подключениями

В реальном приложении нужно хранить список активных соединений. Используйте ConcurrentDictionary или ConcurrentBag:

public class WebSocketConnectionManager
{
    private readonly ConcurrentDictionary<string, WebSocket> _sockets = new();

    public string AddSocket(WebSocket socket)
    {
        var id = Guid.NewGuid().ToString();
        _sockets.TryAdd(id, socket);
        return id;
    }

    public async Task BroadcastMessageAsync(string message)
    {
        var tasks = _sockets.Values
            .Where(s => s.State == WebSocketState.Open)
            .Select(async socket =>
            {
                var data = Encoding.UTF8.GetBytes(message);
                await socket.SendAsync(
                    new ArraySegment<byte>(data), 
                    WebSocketMessageType.Text, 
                    true, 
                    CancellationToken.None);
            });
        await Task.WhenAll(tasks);
    }

    public void RemoveSocket(string id)
    {
        _sockets.TryRemove(id, out _);
    }
}

WebSocket клиент на C# (.NET)

Клиентская часть может быть как консольным приложением, так и частью веб-интерфейса (JavaScript). Рассмотрим создание C#-клиента для тестирования сервера.

using System.Net.WebSockets;
using System.Text;

class WebSocketClient
{
    static async Task Main(string[] args)
    {
        using var client = new ClientWebSocket();
        var uri = new Uri("ws://localhost:5000/ws");
        
        await client.ConnectAsync(uri, CancellationToken.None);
        Console.WriteLine("Подключено к серверу");

        // Отправляем сообщение
        var sendBytes = Encoding.UTF8.GetBytes("Привет, WebSocket!");
        await client.SendAsync(
            new ArraySegment<byte>(sendBytes), 
            WebSocketMessageType.Text, 
            true, 
            CancellationToken.None);

        // Получаем ответ
        var buffer = new byte[1024 * 4];
        var result = await client.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);
        
        var response = Encoding.UTF8.GetString(buffer, 0, result.Count);
        Console.WriteLine($"Ответ сервера: {response}");

        await client.CloseAsync(
            WebSocketCloseStatus.NormalClosure, 
            "Client closing", 
            CancellationToken.None);
    }
}

Для работы с клиентом в вебе используйте стандартный JavaScript WebSocket API. Взаимодействие с C#-сервером будет прозрачным.

Лучшие практики и оптимизация real-time приложений

При разработке production-решений учитывайте следующие аспекты:

  • Управление жизненным циклом соединений: всегда обрабатывайте закрытие WebSocket, освобождайте ресурсы, удаляйте мёртвые соединения из менеджера.
  • Безопасность: используйте WSS (WebSocket Secure) через HTTPS, проверяйте Origin заголовки, реализуйте аутентификацию (JWT токены в query string).
  • Масштабирование: для распределённых систем используйте бэкплейны (Redis, Azure SignalR Service) для синхронизации между экземплярами приложения.
  • Обработка ошибок: обора

Блоги

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