Выбор размера буфера для ответа 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
,