c# stream io network-protocols buffered

c# - ¿Está Stream.Read almacenado en búfer al hacer E/S de red?



io network-protocols (3)

Así que recientemente estuve trabajando, cuando alguien me dijo que si estaba haciendo un Stream.Read en una transmisión de red que se obtiene al llamar a uno de .NET GetResponseStream en WebResponse o que están almacenados en el búfer.

Él estaba diciendo que si pusiera un punto de interrupción, en el código en el que está leyendo, no detendría el tráfico de la red. Lo encuentro bizarro, pero también espero que sea cierto. ¿Cómo funciona? ¿Es incluso exacto?

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


Esto es exacto TCP se implementa mediante la pila del controlador TCP / IP de Windows. Establecer un punto de interrupción en su programa no impide que el controlador descargue datos del servidor. No hasta que el controlador decida que se está usando demasiado espacio en el kernel para almacenar los datos. Las reglas exactas para las cuales no están documentadas.

Esta es una optimización, una estándar en los sistemas operativos. La estrategia hace que las transferencias TCP sean muy eficientes, no depende de cuán receptivo sea su programa, solo por el ancho de banda de la conexión y cuán receptivo es la pila de controladores a las interrupciones de la tarjeta de red. Para lo que es muy bueno, es un trabajo de conductor.


No, el objeto Stream devuelto por GetResponseStream no está almacenado en el búfer.

La respuesta breve a su segunda parte (sobre establecer un punto de interrupción) es que su compañero de trabajo es incorrecto. El tráfico de la red se detendrá, pero eventualmente, y para describir "eventualmente", siga leyendo para obtener más detalles.

Bing para "SO_RCVBUF", "tcp receive window size", "vista auto scaleing", para obtener más información general.

Parte detallada

Comencemos con esto, aquí hay una vista textual de la pila de red de Windows:

++ .NET Red API''s

++ --- Winsock DLL (modo de usuario)

++ ------ afd.sys (modo kernel)

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

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

++ --------------- interfaz de red (hal)

Esta es una pila aproximada, pasando por alto algunos detalles, pero la idea general es que .NET llama al WinLook en modo de usuario dll, que luego envía la mayor parte del trabajo real a su primo AFD (controlador de función auxiliar), en adelante al sub tcpip sistema, etc.

En el nivel AFD , hay un buffer, generalmente entre 8K y 64K , pero con Vista (y más allá), también puede escalar. Esta configuración también se puede controlar mediante una configuración de registro ( HKLM / SYSTEM / CurrentControlSet / services / AFD / Parameters ).

Además, el tcpip.sys también tiene un buffer, que es similar al buffer de AFD. Creo que el ajuste * SO_RCVBUF * pasado al abrir el socket también puede cambiar esto.

Básicamente, cuando recibe datos, tcpip.sys en su nombre sigue obteniendo datos, y le dice al remitente que obtuvo los datos ( ACK ), y lo hace hasta que sus búferes estén llenos. Pero, al mismo tiempo, afd.sys está borrando los búferes tcpip.sys pidiéndole los datos (que luego copia en su propio búfer), por lo que tcpip.sys puede llenar más datos del remitente.

Y luego está usted (el llamante .NET API), que también está haciendo lo mismo, llamando al método Read () y copiando datos en su búfer.

Entonces, si lo piensas, un mensaje de 256Kb viene por el cable, 64K en el búfer tcpip.sys , 64K en el búfer de afd.sys , y configura un punto de interrupción después de pedir un fragmento de 4K (tu variable de tamaño de búfer), estamos viendo 128K ACK devuelto al remitente tal como se recibió, y dado que el búfer tcpip.sys está lleno (suponiendo un tamaño de 64 KB) ahora (y su sesión de depuración lo bloquea), tcpip.sys no tendrá opción pero para decirle al remitente que deje de enviar bytes por el cable, porque no puede procesarlos lo suficientemente rápido.

Prácticamente (es decir, ¡alguien que no establece un punto de inflexión!), He visto a GC inducir tal comportamiento. Se ha visto un caso de una recolección de basura de 3 segundos que permite llenar todos los búferes del sistema operativo.


Un NetworkStream no está almacenado de forma predeterminada. Cuando coloca un punto de interrupción dentro del procedimiento que está leyendo esta secuencia, el cliente que está enviando datos al socket subyacente se bloqueará y esperará hasta que el socket remoto esté listo para recibir nuevamente. El cliente no podrá escribir en el socket, entonces, sí, el tráfico de la red se detiene .

Aquí hay una publicación de blog que ilustra cómo puede hacer que esté protegida mediante el uso de la clase BufferedStream .