El rendimiento lento se produce cuando se copian datos en un servidor TCP mediante un programa de API de Windows Sockets

En este artículo se proporcionan soluciones alternativas para el problema en el que se produce un rendimiento lento al copiar datos en un servidor TCP mediante un programa de API de Windows Sockets.

Se aplica a: Windows Server 2012 R2, Windows 10, todas las ediciones
Número de KB original: 823764

Síntomas

Al ejecutar un programa que usa la API de Windows Sockets, puede experimentar un rendimiento lento al copiar datos en un servidor TCP.

Si realiza un seguimiento de red con un rastreador de red como Microsoft Network Monitor, el servidor TCP envía un segmento TCP ACK al último segmento TCP de un flujo de datos TCP en el temporizador de confirmación retrasada (también conocido como temporizador 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ípico para enviar 64 kilobytes (KB) de datos es similar a la secuencia siguiente:

Client-Server> 1460 bytes
Client-Server> 1460 bytes
ACK servidor-cliente>
Client-Server> 1460 bytes
Client-Server> 1460 bytes
ACK servidor-cliente>
....
Client-Server> 1460 bytes
Client-Server> 1460 bytes
Servidor-cliente> ACK-PUSH
Client-Server> 1296 bytes
-> retraso de ACK 200 ms

Causa

Este problema se produce debido al comportamiento arquitectónico de la API de Windows Sockets y afd.sys. Este problema se produce si se cumplen todas las condiciones siguientes:

  • El programa Windows Sockets usa sockets sin bloqueo.

  • Una única llamada de envío o llamada WSASend rellena todo el búfer de envío de socket subyacente.

    Por ejemplo, el programa usa la función Windows Sockets setsockopt 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 adelante, cuando el programa envía datos, emite una llamada de envío 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 el programa emite una llamada de envío de 64 KB de datos, el programa devuelve un código de error SOCKET_ERROR si se rellena el búfer de socket de 32 KB subyacente. Después de llamar a la función WSAGetLastError, el programa recibe el código de error WSAEWOULDBLOCK. La mayoría de los programas usan la función de selección de Windows Sockets para comprobar el estado del socket. En este escenario, la función select no notifica el socket como grabable hasta que el cliente recibe el segmento TCP ACK pendiente. De forma predeterminada en un entorno de Windows, esto puede tardar hasta 200 ms debido al algoritmo de confirmación retrasada.

  • El servidor TCP remoto confirma todos los segmentos TCP antes de que el cliente envíe el último segmento TCP con el conjunto de bits de inserción.

Solución alternativa

Para solucionar este problema, use cualquiera de los métodos siguientes.

Método 1: Uso de sockets de bloqueo

Este problema solo se produce con sockets sin bloqueo. Cuando se usa un socket de bloqueo, este problema no se produce porque afd.sys controla el búfer de socket de forma diferente. Para obtener más información sobre la programación de socket de bloqueo y no bloqueo, consulte la documentación del SDK de plataforma de Microsoft.

Método 2: Hacer que el tamaño del búfer de envío de socket sea mayor que el tamaño del búfer de envío del programa

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

Modifique la llamada de envío o la llamada WSASend para especificar un tamaño de búfer de al menos 1 byte menor que el valor de SO_SNDBUF. En el ejemplo anterior de la sección "Causa" de este artículo, podría modificar la llamada a setsockopt al siguiente valor,

setsockopt( sock, SOL_SOCKET, 65537, (char *) &val, sizeof( int ));

o bien podría modificar la llamada de envío al siguiente valor:

send(socket, pWrBuffer, 32767, 0);

También puede usar cualquier combinación de estos valores.

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

Importante

Esta sección, método o tarea contiene pasos que le indican cómo modificar el Registro. No obstante, pueden producirse problemas graves si modifica el registro de manera incorrecta. En consecuencia, asegúrese de seguir estos pasos cuidadosamente. Para mayor protección, cree una copia de seguridad del registro antes de modificarlo. Después, puede restaurar el registro si se produce un problema. Para obtener más información acerca de cómo realizar una copia de seguridad y restaurar el Registro, haga clic en el número de artículo siguiente para verlo en Microsoft Knowledge Base:
322756 Hacer una copia de seguridad del Registro y restaurarlo en Windows

Modifique la configuración de TCP/IP en el servidor TCP para confirmar inmediatamente los segmentos TCP entrantes. Esta solución alternativa 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. En escenarios en los que el servidor TCP remoto se ejecuta 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 sobre cómo cambiar el temporizador de confirmación retrasada.

En un servidor que ejecuta Windows 2000, siga estos pasos:

  1. Inicie Editor del Registro (Regedit.exe).
  2. Busque la siguiente subclave del Registro y haga clic en ella: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. En el menú Editar , haga clic en Agregar valor y, a continuación, cree el siguiente valor del Registro:
    Nombre del valor: TcpDelAckTicks
    Tipo de datos: REG_DWORD
    Datos de 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 la siguiente subclave del Registro y haga clic en ella: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. En el menú Edición, seleccione Nuevo y haga clic en Valor DWORD.
  4. Asigne al nuevo valor el nombre TcpAckFrequency y 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, método o tarea contiene pasos que le indican cómo modificar el Registro. No obstante, pueden producirse problemas graves si modifica el registro de manera incorrecta. En consecuencia, asegúrese de seguir estos pasos cuidadosamente. Para mayor protección, cree una copia de seguridad del registro antes de modificarlo. Después, puede restaurar el registro si se produce un problema. Para obtener más información sobre cómo hacer una 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 una copia de seguridad y restaurar el Registro en Windows.

Nota:

Esta clave del Registro solo está disponible para Windows Server 2003 con Service Pack 1 y los Service Pack posteriores.

  1. Haga clic en Inicio, escriba regedit.exey, a continuación, haga clic en Aceptar.
  2. Busque la siguiente subclave del registro y haga clic en ella:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. En el menú Edición, seleccione Nuevo y haga clic en Valor DWORD.
  4. Asigne al nuevo valor el nombre NonBlockingSendSpecialBuffering y 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 enumerados en la sección "Se aplica a".

Referencias

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