System.IO.Stream.Read застревает - PullRequest
       33

System.IO.Stream.Read застревает

1 голос
/ 15 сентября 2011

У меня HTTP-соединение со стороной сервера с использованием System.IO.Stream.Read для чтения сообщения тела HTTP-запроса.Проблема заключается в том, что раз в пару минут сервер застревает в операторе Read и не продолжает работу, пока не истечет время ожидания сокета или клиент не закрыл соединение.

int bytesRead = 0;

while (bytesRead < contentLength)
{
  int got = stream.Stream.Read(buffer.Buffer, bytesRead, contentLength - bytesRead);
  bytesRead += got;
}
  1. Это может произойти, если поток не имеет объема данных, указанного в переменной contentLength.Это не так, потому что при следовании по tcp-потоку с помощью WireShark я вижу, что все тело сообщения (как указано в contentLength) достигло сервера.

  2. Это происходит только в первомвремя, когда цикл while был «использован», т. е. только в первый раз, когда поток не имел числа «contentLength» для чтения за одну попытку, и цикл while должен был быть повторно введен.

Почему он застревает и не продолжает чтение данных?

Ответы [ 4 ]

1 голос
/ 15 сентября 2011

Интересно, сообщает ли поток о досрочном завершении; Вы также должны посмотреть, вернул ли Read неположительное число, т.е.

while (bytesRead < contentLength)
{
  int got = stream.Stream.Read(
       buffer.Buffer, bytesRead, contentLength - bytesRead);
  if(got <= 0) throw new EndOfStreamException(string.Format(
       "Expected {0} bytes; {1} bytes received", contentLength, bytesRead));
  bytesRead += got;
}

По сути, если поток закрылся, каждый вызов Read будет возвращать неположительное значение (вероятно, 0) - так что ваш цикл while станет узким циклом "прочитайте 0, добавьте 0, чтение 0, добавление 0, чтение 0, добавление 0, чтение 0, добавление 0 ".

В качестве последнего замечания ваш подход предполагает, что вы выделяете byte[] на основе входящего заголовка длины содержимого; только предупреждение: убедитесь, что вы проверяете это и ограничиваете его нормальными значениями, иначе атака DOS тривиальна. Кроме того, если возможно Я бы предложил по возможности использовать потоковый API, чтобы избегать необходимости загружать все это в память сразу (если вы не ограничили входящий размер таким образом, чтобы это не беспокойство).

0 голосов
/ 19 сентября 2011

Похоже, это как-то связано с тем, что я использовал метод Read как NetworkStream (это вызов, показанный в приведенном выше коде), так и StreamReader он был создан путем передачи объекта NetworkStream его конструктору.

NetworkStream.Read использовался для чтения тела сообщения запроса http, а StreamReader.Read использовался для чтения остальной части запроса (начальная строка, заголовки...).Хотя вызовы происходили синхронно одним и тем же потоком, это могло быть причиной поведения, с которым я столкнулся.

При изменении кода для работы только с сокетом и выполнения чтения непосредственно из сокета, он исправилпроблема.

0 голосов
/ 15 сентября 2011

У меня были подобные ошибки только с клиентами, которые не очищали свои потоки. Документ MSDN на System.IO.Stream.Read гласит: «Реализация будет блокироваться до тех пор, пока не будет прочитан хотя бы один байт данных, в случае, если данные недоступны». Так что по некоторым причинам нет доступных данных. Я думаю, что вы могли бы установить определенный ReadTimeout и перестать ждать больше данных через достаточно короткое время.

Соответствующий вопрос также был размещен здесь: C # NetworkStream.Read oddity . Может быть, его решение может помочь вам.

0 голосов
/ 15 сентября 2011

Попробуйте эту реализацию, ваш счет (contentLength - bytesRead) неверен.Это должен быть размер буфера.

byte[] buffer = new byte[bufferSize];
int count;
while ((count = stream.Stream.Read(buffer, 0, buffer.Length)) != 0)
{ 
    // do something with the buffer using count as the end marker
    destination.Write(buffer, 0, count);
}

Если вам нужен только байтовый массив из потока, который больше похож на то, что вы пытаетесь:

byte[] buffer = stream.Stream.ToArray()

Или скопировать в другой буфер:

byte[] data = stream.Stream.ToArray();
Array.CopyTo(data , buffer.Buffer, data.Length)
...