Является ли Stream.Read буферизированным при выполнении сетевых операций ввода-вывода?

3 halivingston [2012-01-20 01:32:00]

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

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

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?
   }
}

c# io stream network-protocols


3 ответа


5 Решение mjsabby [2012-01-20 10:46:00]

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

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

Bing для "SO_RCVBUF", "tcp получить размер окна", "автоматическое масштабирование vista", для получения более общей информации.

Подробная часть

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

++.NET Network API

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

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

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

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

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

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

На уровне AFD существует буфер, обычно между 8K и 64K, но с Vista (и за его пределами) он также может увеличиваться. Этот параметр также можно контролировать с помощью параметра реестра (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), мы смотрим на 128K ACK'ed обратно отправителю в том виде, в котором он был получен, и поскольку буфер tcpip.sys заполнен (предполагается, что размер 64K) теперь (и вы заблокированы сеансом отладки), tcpip.sys будет нет опции, но чтобы сообщить отправителю прекратить отправку байтов по проводу, потому что он не может обработать их достаточно быстро.

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


2 Hans Passant [2012-01-20 05:10:00]

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

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


0 Darin Dimitrov [2012-01-20 01:57:00]

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

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