c# - protocol - stream class




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

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

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

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

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

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

++ API .NET Network API

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

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

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

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

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

Это грубый стек, замаскировавший некоторые детали, но общая идея заключается в том, что .NET вызывает в dll Winsock user-mode, который затем толкает большую часть реальной работы на своего двоюродного брата AFD (вспомогательный драйвер функции) и далее на sub 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 может заполнять больше данных от отправителя.

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

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

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

Поэтому я недавно занимался какой-то работой, когда кто-то сказал мне, что если вы делаете Stream.Read в сетевом потоке, который получается при вызове одного из GetResponseStream .NET в 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?
   }
}

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

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





buffered