Stream.Read буферизируется при выполнении сетевого ввода-вывода? - PullRequest
3 голосов
/ 20 января 2012

Итак, я недавно делал какую-то работу, когда кто-то сказал мне, что если Stream.Read выполняется в сетевом потоке, который получается при вызове одного из .NET GetResponseStream на WebResponse или они буферизируются.*

Он говорил, что если вы установите точку останова в коде, который вы читаете, вы не остановите сетевой трафик.Я нахожу эту причуду, но также надеюсь, что это правда.Как это работает?Это даже точно?

using (Stream webResponseStream = this.webResponse.GetResponseStream())
{
   byte[] readBuffer = new byte[bufferSize];
   int bytesRead = webResponseStream.Read(readBuffer, 0, bufferSize);
   while (bytesRead > 0)
   {
        bytesRead = webResponseStream.Read(readBuffer, 0, bufferSize);
        // If I put a breakpoint here, does network activity stop?
   }
}

Ответы [ 3 ]

5 голосов
/ 20 января 2012

Нет, объект Stream, возвращаемый GetResponseStream , не буферизуется.

Краткий ответ на вторую часть (о настройке точки останова) состоит в том, что ваш сотрудник неверен.Сетевой трафик остановится, но, в конце концов, и для описания «в конце концов», читайте дальше для более подробной информации.

Bing для «SO_RCVBUF», «Размер окна приема tcp», «Автоматическое масштабирование перспективы»", для еще более общей информации.

Детальная часть

Начнем с этого, вот текстовое представление сетевого стека Windows:

++ .NET Network API's

++ --- Winsock DLL (пользовательский режим)

++ ------ afd.sys (режим ядра)

++ --------- tcpip.sys

++ ------------ ndis

++ --------------- сетевой интерфейс (hal)

Это грубый стек, приукрашивающий некоторые детали, но общая идея заключается в том, что .NET вызывает Winllck в пользовательском режиме dll, который затем выталкивает большинствонастоящая работа его двоюродного брата AFD (Драйвер вспомогательных функций), далее в подсистему tcpip, и т. д.

На уровне AFD естьбуфер, обычно между 8K и 64K , но с Vista (и beyond), он также может увеличиваться.Этот параметр также может управляться параметром реестра ( HKLM \ SYSTEM \ CurrentControlSet \ services \ AFD \ Parameters ).

Кроме того, tcpip.sys также имеет буфер, то естьпохож на буфер AFD.Я полагаю, что параметр * SO_RCVBUF *, переданный при открытии сокета, также может изменить это.

По сути, когда вы получаете данные, tcpip.sys от вашего имени продолжает получать данные и сохраняет сообщениеотправитель, который получил данные ( ACK ), и делает это до тех пор, пока его буферы не будут заполнены.Но в то же время afd.sys очищает буферы tcpip.sys , запрашивая данные (которые затем копирует в свой собственный буфер), поэтому tcpip.sys может заполнить больше данных от отправителя.

А вот и вы (вызывающий .NET API), который также делает то же самое, вызывает метод Read () и копирует данные в ваш буфер.

Итак, если вы подумаете об этом, сообщение 256Kb приходит по проводам, 64K в буфере tcpip.sys , 64K в буфере afd.sys и вы устанавливаете точку останова после запроса одного 4K (вашей переменной bufferSize) чанка, мы смотрим на ACK, переданный 128K, обратно отправителю, как получено, и так как буфер tcpip.sys заполнен (принимая размер 64 КБ) (и вы заблокированы сеансом отладки), tcpip.sys не будет иметь другого выбора, кроме как сказать отправителю прекратить отправку байтов по проводам, потому что он не может обработатьдостаточно быстро.

Практически (т.е. кто-то не настраиваетРеакпоинт!), я видел GC, чтобы вызвать такое поведение.Видел случай 3-секундной сборки мусора, которая позволяла заполнить все буферы ОС.

2 голосов
/ 20 января 2012

Это точно. TCP реализуется стеком драйверов Windows TCP / IP. Установка точки останова в вашей программе не мешает драйверу загружать данные с сервера. Пока драйвер не решит, что слишком много места в пуле ядра используется для буферизации данных. Точные правила для которых не документированы.

Это стандартная для операционных систем оптимизация. Стратегия делает передачу TCP очень эффективной, она не зависит от того, насколько отзывчива ваша программа, только от пропускной способности соединения и от того, насколько стог драйверов реагирует на прерывания сетевой карты. Что это очень хорошо, это работа водителя.

0 голосов
/ 20 января 2012

A NetworkStream по умолчанию не буферизуется.Когда вы помещаете точку останова в процедуру, которая читает этот поток, клиент, который отправляет данные в базовый сокет, будет блокировать и ждать, пока удаленный сокет снова не будет готов к приему.Клиент не сможет писать в сокет, так что да, сетевой трафик останавливается .

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

...