O desempenho lento ocorre quando você copia dados para um servidor TCP usando um programa de API do Windows Sockets

Este artigo fornece soluções alternativas para o problema em que o desempenho lento ocorre quando você copia dados para um servidor TCP usando um programa de API do Windows Sockets.

Aplica-se a: Windows Server 2012 R2, Windows 10 - todas as edições
Número de KB original: 823764

Sintomas

Ao executar um programa que usa a API do Windows Sockets, você pode ter um desempenho lento ao copiar dados para um servidor TCP.

Se você fizer um rastreamento de rede com um farejador de rede, como o Microsoft Network Monitor, o servidor TCP enviará um segmento TCP ACK para o último segmento TCP em um fluxo de dados TCP no temporizador de reconhecimento atrasado (também conhecido como temporizador ACK atrasado). Por padrão, para sistemas operacionais Windows, o valor para esse temporizador é de 200 milissegundos (ms). Um fluxo de dados típico para o envio de 64 quilobytes (KB) de dados é semelhante à seguinte sequência:

Bytes do Client-Server> 1460
Bytes do Client-Server> 1460
ACK servidor-cliente>
Bytes do Client-Server> 1460
Bytes do Client-Server> 1460
ACK servidor-cliente>
....
Bytes do Client-Server> 1460
Bytes do Client-Server> 1460
ACK-PUSH servidor-cliente>
Bytes do Client-Server> 1296
-> ACK 200 ms atrasada

Motivo

Esse problema ocorre devido ao comportamento arquitetônico da API e do afd.sys do Windows Sockets. Esse problema ocorrerá se todas as seguintes condições forem verdadeiras:

  • O programa Soquetes do Windows usa soquetes que não bloqueiam.

  • Uma única chamada de envio ou chamada WSASend preenche todo o buffer de envio de soquete subjacente.

    Por exemplo, o programa usa a função Soquetes setsockopt do Windows para alterar o buffer de envio de soquete padrão para 32 KB durante suas rotinas de inicialização de soquete:

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

    Posteriormente, quando o programa envia dados, ele emite uma chamada de envio ou uma chamada WSASend e envia 64 KB de dados durante cada envio:

    send(socket, pWrBuffer, 65536, 0);
    

    Nesse cenário, sempre que o programa emite uma chamada de envio de 64 KB de dados, o programa retorna um código de erro SOCKET_ERROR se o buffer de soquete de 32 KB subjacente for preenchido. Depois de chamar a função WSAGetLastError, o programa recebe o código de erro WSAEWOULDBLOCK. A maioria dos programas usa a função selecionar Soquetes do Windows para marcar o status do soquete. Nesse cenário, a função select não relata o soquete como gravável até que o cliente receba o segmento TCP ACK pendente. Por padrão em um ambiente windows, isso pode levar até 200 ms devido ao algoritmo de reconhecimento atrasado.

  • O servidor TCP remoto reconhece todos os segmentos TCP antes que o cliente envie o último segmento TCP com o conjunto de bits push.

Solução alternativa

Para contornar esse problema, use qualquer um dos métodos a seguir.

Método 1: usar soquetes de bloqueio

Esse problema ocorre apenas com soquetes que não bloqueiam. Quando você usa um soquete de bloqueio, esse problema não ocorre porque afd.sys manipula o buffer do soquete de forma diferente. Para obter mais informações sobre como bloquear e não bloquear a programação do soquete, confira a documentação do SDK da Plataforma Microsoft.

Método 2: tornar o tamanho do buffer de envio do soquete maior que o tamanho do buffer de envio do programa

Para modificar o buffer de envio do soquete, use a função Soquetes getsockopt do Windows para determinar o tamanho do buffer de envio do soquete atual (SO_SNDBUF) e use a setsockopt função para definir o tamanho do buffer de envio do soquete. Quando você terminar, o valor SO_SNDBUF deve ser pelo menos 1 byte maior que o tamanho do buffer de envio do programa.

Modifique a chamada de envio ou a chamada WSASend para especificar um tamanho de buffer pelo menos 1 byte menor que o valor SO_SNDBUF. No exemplo anterior na seção "Causa" deste artigo, você pode modificar a chamada setsockopt para o valor a seguir,

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

ou você pode modificar a chamada de envio para o seguinte valor:

send(socket, pWrBuffer, 32767, 0);

Você também pode usar qualquer combinação desses valores.

Método 3: modificar as configurações TCP/IP no servidor TCP

Importante

Esta seção, método ou tarefa contém etapas que descrevem como modificar o Registro. Entretanto, sérios problemas poderão ocorrer caso você modifique o Registro incorretamente. Portanto, siga essas etapas cuidadosamente. Para mais proteção, faça o backup do registro antes de modificá-lo. Em seguida, você poderá restaurar o registro se ocorrer um problema. Para obter mais informações sobre como fazer backup e restaurar o Registro, clique no número abaixo para ler o artigo na Base de Dados de Conhecimento Microsoft:
322756 Como fazer o backup e a restauração do Registro no Windows

Modifique as configurações TCP/IP no servidor TCP para reconhecer imediatamente os segmentos TCP de entrada. Essa solução alternativa funciona melhor em um ambiente que tem uma base de instalação de cliente grande e em que você não pode alterar o comportamento do programa. Para cenários em que o servidor TCP remoto é executado em um servidor baseado em Windows, você deve modificar o registro do servidor remoto. Para outros sistemas operacionais, consulte a documentação do sistema operacional para obter informações sobre como alterar o temporizador de reconhecimento atrasado.

Em um servidor que executa o Windows 2000, siga estas etapas:

  1. Iniciar Editor do Registro (Regedit.exe).
  2. Localize e clique na seguinte subchave do Registro: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. No menu Editar , clique em Adicionar Valor e crie o seguinte valor de registro:
    Nome do valor: TcpDelAckTicks
    Tipo de dados: REG_DWORD
    Dados de valor: 0
  4. Saia do Editor do Registro.
  5. Reinicie o Windows para que essa alteração entre em vigor.

Em um servidor que executa o Windows XP ou o Windows Server 2003, siga estas etapas:

  1. Inicie o Editor do Registro.
  2. Localize e clique na seguinte subchave do Registro: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. No menu Editar, aponte para Novo e clique em Valor DWORD.
  4. Nomeie o novo valor TcpAckFrequency e atribua um valor de 1.
  5. Saia do Editor do Registro.
  6. Reinicie o Windows para que essa alteração entre em vigor.

Método 4: modificar o comportamento de buffer em afd.sys para soquetes não bloqueados

Importante

Esta seção, método ou tarefa contém etapas que descrevem como modificar o Registro. Entretanto, sérios problemas poderão ocorrer caso você modifique o Registro incorretamente. Portanto, siga essas etapas cuidadosamente. Para mais proteção, faça o backup do registro antes de modificá-lo. Em seguida, você poderá restaurar o registro se ocorrer um problema. Para obter mais informações sobre como fazer backup e restaurar o registro, clique no seguinte número de artigo para exibir o artigo na Base de Dados de Conhecimento da Microsoft: 322756 Como fazer backup e restaurar o registro no Windows

Observação

Essa chave de registro só está disponível para o Windows Server 2003 com o Service Pack 1 e os pacotes de serviço subsequentes.

  1. Clique em Iniciar, digite regedit.exee clique em OK.
  2. Localize e clique na seguinte subchave do Registro:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. No menu Editar, aponte para Novo e clique em Valor DWORD.
  4. Nomeie o novo valor NonBlockingSendSpecialBuffering e atribua um valor de 1.
  5. Sair do Registro Editor.
  6. Reinicie o Windows para que essa alteração entre em vigor.

Status

A Microsoft confirmou que este é um problema nos produtos Microsoft listados na seção "Aplicável a".

Referências

328890 Nova entrada de registro para controlar o comportamento de Reconhecimento TCP (ACK) no Windows XP e no Windows Server 2003