Выбор размера буфера для ответа WebSocket

Я пишу приложение на C#, которое подключается к серверу веб-сокетов и получает ответ JSON неизвестного размера. Я использую ClientWebSocket класс для этой цели.

Единственный способ получить данные от клиента - это ReceiveAsync метод, который принимает ArraySegment<byte> в качестве аргумента:

var client = new ClientWebSocket();
client.ConnectAsync(new Uri($"ws://localhost:{port}"), token).Wait();
var result = new ArraySegment<byte>(new byte[1000]);
client.ReceiveAsync(result, token).Wait();

Проблема в том, что, поскольку я не знаю, насколько большим будет ответ JSON, я не знаю, насколько большим будет буфер, поддерживающий этот ArraySegment. В этом случае я указал 1000 байтов, что слишком мало, и ответ усекается. Однако меня беспокоит, что если я установлю размер буфера произвольно большим (1 000 000 байт?), Я буду использовать больше памяти, чем мне нужно.

Как выбрать размер буфера, не зная размера ответа?

1 ответ

Решение

Если я правильно понимаю API, он при необходимости выдаст вам сообщение websocket из нескольких частей.

Это означает, что если сообщение, отправленное с сервера, имеет размер 2048 байт, а вы используете буфер размером 1024 байт и выполните:

var buffer = new ArraySegment<byte>(new byte[1000]);
var result = await client.ReceiveAsync(buffer, token);

Тогда в этом первом звонке result.Count будет установлен на 1000 и result.EndOfMessage будет установлен в false, Это означает, что вам нужно продолжить чтение до EndOfMessage имеет значение true, что означает 3 чтения для этого примера.

Если вам нужно все в одном буфере и вы не можете самостоятельно обрабатывать фрагменты сообщения, вы можете начать с небольшого буфера и изменить размер приемного буфера, если в результате вы узнаете, что поступает больше данных. Таким образом, вы также можете проверить, что если превышен максимальный размер, прием прекращен.

int bufferSize = 1000;
var buffer = new byte[bufferSize];
var offset = 0;
var free = buffer.Length;
while (true)
{
    var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer, offset, free), token);
    offset += result.Count;
    free -= result.Count;
    if (result.EndOfMessage) break;
    if (free == 0)
    {
        // No free space
        // Resize the outgoing buffer
        var newSize = buffer.Length + bufferSize;
        // Check if the new size exceeds a limit
        // It should suit the data it receives
        // This limit however has a max value of 2 billion bytes (2 GB)
        if (newSize > maxFrameSize)
        {
            throw new Exception ("Maximum size exceeded");
        }
        var newBuffer = new byte[newSize];
        Array.Copy(buffer, 0, newBuffer, 0, offset);
        buffer = newBuffer;
        free = buffer.Length - offset;
    }
}

И, конечно же, вы должны также проверить другие поля в результате получения, как MessageType,

Другие вопросы по тегам