Se produce un rendimiento lento al copiar datos a un servidor TCP utilizando un programa de API de Windows Sockets

Se aplica a: Microsoft Windows Server 2003 Enterprise Edition (32-bit x86)Microsoft Windows Server 2003 Standard Edition (32-bit x86)Microsoft Windows Server 2003 Web Edition

Síntomas


Cuando ejecuta un programa que utiliza la API de Sockets de Windows, puede experimentar un rendimiento lento al copiar datos a un servidor TCP.

Si realiza una traza de red con un rastreador de red, como Monitor de red de Microsoft, el servidor TCP envía un ACK de TCP al último segmento segmento TCP en una secuencia de datos TCP en el temporizador de confirmación retardada (también conocido como el temporizador de ACK retrasado). De forma predeterminada, para los sistemas operativos Windows, el valor de este temporizador es de 200 milisegundos (ms). Un flujo de datos típica para el envío de 64 kilobytes (KB) de datos similar a la siguiente secuencia:
Client->Server 1460 bytesClient->Server 1460 bytes
Server->Client ACK
Client->Server 1460 bytes
Client->Server 1460 bytes
Server->Client ACK
....
Client->Server 1460 bytes
Client->Server 1460 bytes
Server->Client ACK-PUSH
Client->Server 1296 bytes
-> delayed ACK 200 ms



Causa


Este problema se produce debido al comportamiento de la arquitectura de la API de Windows Sockets y Afd.sys. Este problema se produce si se cumplen todas las condiciones siguientes:

  • El programa de Windows Sockets utiliza sockets sin bloqueo.
  • Una llamada única Enviar o una llamada WSASend rellena el búfer de envío de socket subyacente todo.

    Por ejemplo, el programa utiliza la función setsockopt de Windows Sockets para cambiar el búfer de envío de socket predeterminado a 32 KB durante sus rutinas de inicialización de socket:
    setsockopt( sock, SOL_SOCKET, 32768, (char *) &val, sizeof( int ) );
    Más tarde, cuando el programa envía datos, emite una llamada de Enviar o una llamada WSASend y envía 64 KB de datos durante cada envío:
    send(socket, pWrBuffer, 65536, 0);
    En este escenario, cada vez que los problemas de programa Enviar llamada de 64 KB de datos, el programa devuelve un código de error SOCKET_ERROR si el búfer de 32 KB socket subyacente está completamente llena. Después llama a la función WSAGetLastError , el programa recibe el código de error WSAEWOULDBLOCK . La mayoría de programas utiliza la función de Windows Sockets Seleccionar para comprobar el estado del socket. En este escenario, la función Seleccionar no informa el socket como escritura hasta que el cliente recibe el segmento TCP ACK pendiente. De forma predeterminada, en un entorno Windows, esto puede durar tanto tiempo como 200 ms debido al algoritmo de confirmación retardada.
  • El servidor remoto de TCP reconoce todos los segmentos TCP antes de que el cliente envía el último segmento TCP con el bit push activado.

Solución alternativa


Para evitar este problema, utilice cualquiera de los métodos siguientes.

Método 1: Utilizar Sockets de bloqueo

Este problema sólo ocurre con sockets sin bloqueo. Cuando se utiliza un socket de bloqueo, este problema no se produce porque Afd.sys controla de manera diferente el búfer del socket. Para obtener más información acerca de la programación de socket de bloqueo y sin bloqueo, consulte la documentación de Microsoft Platform SDK.

Método 2: Hacer que el tamaño del búfer de envío de Socket más grande que el tamaño del búfer de envío de programa

Para modificar el búfer de envío de socket, utilice la función de Windows Sockets getsockopt para determinar el socket actual Enviar tamaño del búfer (SO_SNDBUF) y, a continuación, utilice la función setsockopt para establecer el socket enviar tamaño del búfer. Cuando haya terminado, el valor SO_SNDBUF debe ser mayor que el tamaño de búfer de envío de programa al menos 1 byte.

Modifique la llamada Enviar o la llamada WSASend para especificar un búfer de tamaño menor que el valor SO_SNDBUF de al menos 1 byte. En el ejemplo anterior en la sección "Causa" de este artículo, se podría modificar la llamada setsockopt al siguiente valor,
setsockopt( sock, SOL_SOCKET, 65537, (char *) &val, sizeof( int ) );
o bien, se podría modificar la llamada Enviar al siguiente valor:
send(socket, pWrBuffer, 32767, 0);
También puede utilizar cualquier combinación de estos valores.

Método 3: Modificar la configuración de TCP/IP en el servidor TCP

Importante: esta sección, el método o la tarea contiene pasos que indican cómo modificar el registro. Sin embargo, pueden producirse problemas graves si modifica incorrectamente el registro. Por lo tanto, asegúrese de que sigue estos pasos cuidadosamente. Para una mayor protección, haga una copia de seguridad del registro antes de modificarlo. Entonces, puede restaurar el registro si se produce un problema. Para obtener más información acerca de cómo hacer copia de seguridad y restaurar el registro, haga clic en el número de artículo siguiente para verlo en Microsoft Knowledge Base:
322756 cómo hacer copia de seguridad y restaurar el registro en Windows


Modificar la configuración de TCP/IP en el servidor TCP para que reconozcan inmediatamente los segmentos TCP entrantes. Esta solución funciona mejor en un entorno que tiene una base de instalación de cliente grande y donde no se puede cambiar el comportamiento del programa. Para los escenarios donde se ejecuta el servidor TCP remoto en un servidor basado en Windows, debe modificar el registro del servidor remoto. Para otros sistemas operativos, consulte la documentación del sistema operativo para obtener información acerca de cómo modificar el temporizador de confirmación retardada.

En un servidor que ejecuta Windows 2000, siga estos pasos:
  1. Inicie el Editor del registro (Regedit.exe).
  2. Busque y, a continuación, haga clic en la subclave del registro siguiente:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\ < GUID de interfaz >
  3. En el menú Edición , haga clic en Agregar valory, a continuación, cree el siguiente valor del registro:

    Nombre de valor: TcpDelAckTicks
    Tipo de datos: REG_DWORD
    Datos del valor: 0
  4. Salga del Editor del registro.
  5. Reinicie Windows para que este cambio surta efecto.
En un servidor que ejecuta Windows XP o Windows Server 2003, siga estos pasos:
  1. Inicie el Editor del registro.
  2. Busque y, a continuación, haga clic en la subclave del registro siguiente:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\ < GUID de interfaz >
  3. En el menú Edición , seleccione Nuevo y, a continuación, haga clic en Valor DWORD.
  4. Nombre al nuevo valor TcpAckFrequencyy asígnele un valor de 1.
  5. Salga del Editor del registro.
  6. Reinicie Windows para que este cambio surta efecto.

Método 4: Modificar el comportamiento de almacenamiento en búfer en Afd.sys para sockets sin bloqueo

Importante: esta sección, el método o la tarea contiene pasos que indican cómo modificar el registro. Sin embargo, pueden producirse problemas graves si modifica incorrectamente el registro. Por lo tanto, asegúrese de que sigue estos pasos cuidadosamente. Para una mayor protección, haga una copia de seguridad del registro antes de modificarlo. Entonces, puede restaurar el registro si se produce un problema. Para obtener más información acerca de cómo hacer copia de seguridad y restaurar el registro, haga clic en el número de artículo siguiente para verlo en Microsoft Knowledge Base:
322756 cómo hacer copia de seguridad y restaurar el registro en Windows


Nota: Esta clave del registro sólo está disponible para Windows Server 2003 con Service Pack 1 y service packs posteriores.
  1. Haga clic en Inicio, escriba regedit.exey, a continuación, haga clic en Aceptar.
  2. Busque y, a continuación, haga clic en la subclave del registro siguiente:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. En el menú Edición , seleccione Nuevo y, a continuación, haga clic en Valor DWORD.
  4. Nombre al nuevo valor NonBlockingSendSpecialBufferingy asígnele un valor de 1.
  5. Salga del Editor del Registro.
  6. Reinicie Windows para que este cambio surta efecto.

Estado


Microsoft ha confirmado que se trata de un problema de los productos de Microsoft que se enumeran en la sección "Aplicable a".

Referencias


Nueva entrada del registro 328890 para controlar el comportamiento de confirmación TCP (ACK) en Windows XP y en Windows Server 2003