using System.Net.WebSockets; using Vanguard.Shared.Protocol; namespace Vanguard.Server.Realtime; /// Чтение одного кадра протокола из бинарных сообщений WebSocket. internal static class ProtocolFrameReader { public static async Task ReadAsync(WebSocket socket, CancellationToken cancellationToken) { var buffer = new byte[16 * 1024]; using var stream = new MemoryStream(); while (true) { var result = await socket.ReceiveAsync(buffer.AsMemory(), cancellationToken); if (result.MessageType == WebSocketMessageType.Close) return null; if (result.MessageType != WebSocketMessageType.Binary) throw new ProtocolFormatException("Ожидался бинарный кадр."); stream.Write(buffer, 0, result.Count); if (stream.Length > ProtocolConstants.MaxFrameBytes) throw new ProtocolFormatException( $"Кадр больше {ProtocolConstants.MaxFrameBytes} байт."); if (result.EndOfMessage) break; } return ProtocolSerializer.DecodeClient(stream.ToArray()); } } /// Не больше N интентов в секунду с одного соединения. internal sealed class IntentRateLimiter { private readonly int _limit; private readonly TimeProvider _time; private DateTimeOffset _windowStart; private int _count; public IntentRateLimiter(int limit, TimeProvider time) { _limit = limit; _time = time; _windowStart = time.GetUtcNow(); } public bool TryAcquire() { var now = _time.GetUtcNow(); if (now - _windowStart >= TimeSpan.FromSeconds(1)) { _windowStart = now; _count = 0; } return ++_count <= _limit; } }