Метод ReceiveAsync websocket не ожидает всего сообщения

я получаю JSON через websocket. По крайней мере, частично. Используя онлайн-сервис websocket, я получаю полный ответ JSON (вся разметка HTML игнорируется). Когда я смотрю на JSON, который я получаю в своей консоли, я вижу разметку HTML (просмотр ее с помощью средства просмотра HTML во время отладки удаляет HTML), но она резко заканчивается (неполные данные).

мой буфер имеет много места, и я использую async-await для (предположительно) ожидания всего ответа прежде чем продолжить.

private async Task Receive()
{
  var buffer = new byte[4096 * 20];

  while (_socket.State == WebSocketState.Open)
  {
      var response = await _socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

      if (response.MessageType == WebSocketMessageType.Close)
      {
          await
              _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close response received",
                  CancellationToken.None);
      }
      else
      {
          var result = Encoding.UTF8.GetString(buffer);
          var a = buffer[1000];
          var b = buffer[10000];
          var c = buffer[50000];
          var d = buffer[81000];
          Console.WriteLine(result);
          var responseObject = JsonConvert.DeserializeObject<Response>(result, _requestParameters.ResponseDataType);

          OnSocketReceive.Invoke(this, new SocketEventArgs {Response = responseObject });
          buffer = new byte[4096 * 20];
      }
  }
}   

вещи, чтобы отметить: буфер достаточно большой и b, c и d никогда не заполнены. Я также должен отметить, что это происходит только в 1-questions-newest-tag-java запрос 155-questions-active работает прекрасно.

после некоторых раскопок я нашел, что response.CloseStatus и response.CloseStatusDescription всегда null, response.Count всегда 1396 (копирование-вставка результата в Word показывает, что всегда есть 1396 символов) и response.EndOfMessage is false.

копаясь некоторые исходный код я обнаружил, что DefaultReceiveBufferSize is 16 * 1024 (достаточно большой) и WebSocketGetDefaultKeepAliveInterval() относится к внешнего внедрения (но отладчик показывает 00:00:30).

это не вопрос времени ожидания, так как отладчик останавливается в тот же момент, когда онлайн-служба получает свой ответ.

почему мой метод продолжает выполняться, когда гнездо еще не получил все данные?

5 ответов


Я могу ошибаться, но я не думаю, что вы всегда должны получать полный WebSocket сообщение сразу. Сервер может отправлять сообщение кусками (это соответствует вызову SendAsync С endOfMessage: false).

Итак,await _socket.ReceiveAsync() в цикле и накапливать полученные куски, пока WebSocketReceiveResult.EndOfMessage is true или произошла ошибка.

на боковой ноте, вы, вероятно, должны использовать WebSocket.CreateClientBuffer вместо new ArraySegment<byte>(buffer).


чтобы завершить ответ @Noseratio, код будет примерно таким:

ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[8192]);

WebSocketReceiveResult result= null;

using (var ms = new MemoryStream())
{
     do
     {
         result = await socket.ReceiveAsync(buffer, CancellationToken.None);
         ms.Write(buffer.Array, buffer.Offset, result.Count);
     }
     while (!result.EndOfMessage);

     ms.Seek(0, SeekOrigin.Begin);

     if (result.MessageType == WebSocketMessageType.Text)
     {
          using (var reader = new StreamReader(ms, Encoding.UTF8))
          {
               // do stuff
          }
     }
}

Ура.


после ответа Noseratio я реализовал временный буфер, который будет строить данные всего сообщения.

var temporaryBuffer = new byte[BufferSize];
var buffer = new byte[BufferSize * 20];
int offset = 0;
WebSocketReceiveResult response;

while (true)
{
    response = await _socket.ReceiveAsync(
                         new ArraySegment<byte>(temporaryBuffer), 
                         CancellationToken.None);
    temporaryBuffer.CopyTo(buffer, offset);
    offset += response.Count;
    temporaryBuffer = new byte[BufferSize];
    if (response.EndOfMessage)
    {
        break;
    }
}

полной реализации здесь


// Read the bytes from the web socket and accumulate all into a list.
var buffer = new ArraySegment<byte>(new byte[1024]);
WebSocketReceiveResult result = null;
var allBytes = new List<byte>();

do
{
    result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
    for (int i = 0; i < result.Count; i++)
    {
        allBytes.Add(buffer.Array[i]);
    }
}
while (!result.EndOfMessage);

// Optional step to convert to a string (UTF-8 encoding).
var text = Encoding.UTF8.GetString(allBytes.ToArray(), 0, allBytes.Count);

попробуйте это:

try
{
    WebSocketReceiveResult result;
    string receivedMessage = "";
    var message = new ArraySegment<byte>(new byte[4096]);
    do
    {
        result = await WebSocket.ReceiveAsync(message, DisconectToken);
        if (result.MessageType != WebSocketMessageType.Text)
            break;
        var messageBytes = message.Skip(message.Offset).Take(result.Count).ToArray();
        receivedMessage += Encoding.UTF8.GetString(messageBytes);                    
    }
    while (!result.EndOfMessage);
    if (receivedMessage != "{}" && !string.IsNullOrEmpty(receivedMessage))
    {
        ResolveWebSocketResponse.Invoke(receivedMessage, Connection);
        Console.WriteLine("Received: {0}", receivedMessage);
    }
}
catch (Exception ex)
{
    var mes = ex.Message;
}