Des performances lentes se produisent lorsque vous copiez des données sur un serveur TCP à l’aide d’un programme d’API de sockets Windows

Cet article fournit des solutions de contournement pour le problème où des performances lentes se produisent lorsque vous copiez des données sur un serveur TCP à l’aide d’un programme d’API de sockets Windows.

Version du produit d’origine :   Windows Server 2012 R2, Windows 10 - toutes les éditions
Numéro de la ko d’origine :   823764

Symptômes

Lorsque vous exécutez un programme qui utilise l’API de sockets Windows, vous pouvez être ralenti lorsque vous copiez des données sur un serveur TCP.

Si vous tracez le réseau avec un renifleur réseau tel que le moniteur réseau Microsoft, le serveur TCP envoie un segment TCP ACK au dernier segment TCP d’un flux de données TCP dans le délai d’attente d’accusé de réception (également appelé le temps ACK différé). Par défaut, pour les systèmes d’exploitation Windows, la valeur de ce minuteur est de 200 millisecondes (ms). Un flux de données classique pour l’envoi de 64 kilo-octets (Ko) de données ressemble à la séquence suivante :

Client->Server 1460 octets
Client->Server 1460 octets
Server->Client ACK
Client->Server 1460 octets
Client->Server 1460 octets
Server->Client ACK
....
Client->Server 1460 octets
Client->Server 1460 octets
Server->Client ACK-PUSH
Client->Server 1296 octets
-> ACK retardée de 200 ms

Cause

Ce problème se produit en raison du comportement architectural de l’API de sockets Windows afd.sys. Ce problème se produit si toutes les conditions suivantes sont vraies :

  • Le programme Sockets Windows utilise des sockets non bloquants.

  • Un appel d’envoi unique ou un appel WSASend remplit l’intégralité de la mémoire tampon d’envoi du socket sous-jacent.

    Par exemple, le programme utilise la fonction Windows Sockets pour modifier la mémoire tampon d’envoi de socket par défaut à 32 Ko au cours de ses setsockopt routines d’initialisation de socket :

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

    Plus tard, lorsque le programme envoie des données, il émettra un appel d’envoi ou un appel WSASend et enverra 64 Ko de données pendant chaque envoi :

    send(socket, pWrBuffer, 65536, 0);
    

    Dans ce scénario, chaque fois que le programme envoie un appel d’envoi de 64 Ko de données, le programme renvoie un code d’erreur SOCKET_ERROR si la mémoire tampon de socket sous-jacente de 32 Ko est remplie. Après avoir appelé la fonction WSAGetLastError, le programme reçoit le code d’erreur WSAEWOULDBLOCK. La plupart des programmes utilisent la fonction de sélection de sockets Windows pour vérifier l’état du socket. Dans ce scénario, la fonction select ne signale pas le socket comme accessible en tant qu’il n’a pas reçu le segment TCP ACK en suspens. Par défaut, dans un environnement Windows, cela peut prendre jusqu’à 200 ms en raison de l’algorithme d’accusé de réception différé.

  • Le serveur TCP distant reconnaît tous les segments TCP avant que le client envoie le dernier segment TCP avec le bit push.

Solution de contournement

Pour contourner ce problème, utilisez l’une des méthodes suivantes.

Méthode 1 : utiliser des sockets de blocage

Ce problème se produit uniquement avec des sockets non bloquants. Lorsque vous utilisez un socket de blocage, ce problème ne se produit pas car afd.sys la mémoire tampon de socket différemment. Pour plus d’informations sur le blocage et le non-blocage de la programmation de sockets, voir la documentation du SDK de la plateforme Microsoft.

Méthode 2 : faire en sorte que la taille de la mémoire tampon d’envoi du socket soit supérieure à la taille de la mémoire tampon d’envoi du programme

Pour modifier le tampon d’envoi de socket, utilisez la fonction Sockets Windows pour déterminer la taille actuelle de la mémoire tampon d’envoi de socket (SO_SNDBUF), puis utilisez la fonction pour définir la taille de la mémoire tampon d’envoi du getsockopt setsockopt socket. Lorsque vous avez terminé, la SO_SNDBUF doit être supérieure d’au moins 1 byte à la taille de la mémoire tampon d’envoi du programme.

Modifiez l’appel d’envoi ou l’appel WSASend pour spécifier une taille de mémoire tampon d’au moins 1 byte plus petite que la SO_SNDBUF valeur. Dans l’exemple précédent de la section « Cause » de cet article, vous pouviez modifier l’appel setsockopt à la valeur suivante:

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

ou vous pouvez modifier l’appel d’envoi à la valeur suivante :

send(socket, pWrBuffer, 32767, 0);

Vous pouvez également utiliser n’importe quelle combinaison de ces valeurs.

Méthode 3 : modifier les paramètres TCP/IP sur le serveur TCP

Important

Cette section, méthode ou tâche contient des étapes vous indiquant comment modifier le Registre. Toutefois, des problèmes graves peuvent se produire si vous modifiez le Registre de façon incorrecte. Par conséquent, veillez à suivre ces étapes scrupuleusement. Pour une meilleure protection, sauvegardez le registre avant de le modifier. Vous pouvez alors le restaurer en cas de problème. Pour plus d’informations sur la façon de la back up et de la restauration du Registre, cliquez sur le numéro d’article suivant pour afficher l’article dans la Base de connaissances Microsoft :
322756 Comment sauvegarder et restaurer le Registre dans Windows

Modifiez les paramètres TCP/IP sur le serveur TCP pour reconnaître immédiatement les segments TCP entrants. Cette solution de contournement fonctionne mieux dans un environnement qui dispose d’une base d’installation client importante et où vous ne pouvez pas modifier le comportement du programme. Pour les scénarios où le serveur TCP distant s’exécute sur un serveur Windows, vous devez modifier le Registre du serveur distant. Pour les autres systèmes d’exploitation, consultez la documentation du système d’exploitation pour plus d’informations sur la modification du délai d’attente de l’accusé de réception.

Sur un serveur qui exécute Windows 2000, suivez les étapes suivantes :

  1. Démarrez l’Éditeur du Registre (Regedit.exe).
  2. Recherchez, puis cliquez sur la sous-clé de Registre suivante : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. Dans le menu Modifier, cliquez sur Ajouter une valeur, puis créez la valeur de Registre suivante :
    Nom de la valeur : TcpDelAckTicks
    Type de données: REG_DWORD
    Données de valeur: 0
  4. Quittez l’Éditeur du Registre.
  5. Redémarrez Windows pour que cette modification prenne effet.

Sur un serveur qui exécute Windows XP ou Windows Server 2003, suivez les étapes suivantes :

  1. Démarrez l’Éditeur du Registre.
  2. Recherchez, puis cliquez sur la sous-clé de Registre suivante : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. Dans le menu Edition, pointez sur Nouveau, puis cliquez sur Valeur DWORD.
  4. Nommez la nouvelle valeur TcpAckFrequency et attribuez-lui la valeur 1.
  5. Quittez l’Éditeur du Registre.
  6. Redémarrez Windows pour que cette modification prenne effet.

Méthode 4 : Modifier le comportement de mise en mémoire tampon dans afd.sys pour les sockets non bloquants

Important

Cette section, méthode ou tâche contient des étapes vous indiquant comment modifier le Registre. Toutefois, des problèmes graves peuvent se produire si vous modifiez le Registre de façon incorrecte. Par conséquent, veillez à suivre ces étapes scrupuleusement. Pour une meilleure protection, sauvegardez le registre avant de le modifier. Vous pouvez alors le restaurer en cas de problème. Pour plus d’informations sur la façon de la back up et de la restauration du Registre, cliquez sur le numéro d’article suivant pour afficher l’article dans la Base de connaissances Microsoft : 322756 How to back up and restore the registry in Windows

Notes

Cette clé de Registre est disponible uniquement pour Windows Server 2003 avec Service Pack 1 et les Service Packs suivants.

  1. Cliquez sur Démarrer, tapezregedit.exe, puis cliquez sur OK.
  2. Recherchez la sous-clé de Registre suivante, puis cliquez dessus :
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. Dans le menu Edition, pointez sur Nouveau, puis cliquez sur Valeur DWORD.
  4. Nommez la nouvelle valeur NonBlockingSendSpecialBuffering et attribuez-lui la valeur 1.
  5. Quittez l’Éditeur du Registre.
  6. Redémarrez Windows pour que cette modification prenne effet.

Statut

Microsoft a confirmé qu’il s’agit d’un problème dans les produits Microsoft répertoriés dans la section « S’applique à ».

Références

328890 Nouvelle entrée de Registre pour contrôler le comportement de l’accusé de réception TCP (ACK) dans Windows XP et dans Windows Server 2003