Le ralentissement des performances se produit lorsque vous copiez des données vers un serveur TCP à l’aide d’un programme d’API Windows Sockets

Cet article fournit des solutions de contournement pour résoudre le problème lié au ralentissement des performances lorsque vous copiez des données sur un serveur TCP à l’aide d’un programme d’API Windows Sockets.

S’applique à : Windows Server 2012 R2, Windows 10 (toutes les éditions)
Numéro de la base de connaissances d’origine : 823764

Symptômes

Lorsque vous exécutez un programme qui utilise l’API Windows Sockets, les performances peuvent être lentes lorsque vous copiez des données sur un serveur TCP.

Si vous effectuez une trace réseau avec un renifleur réseau tel que Microsoft Network Monitor, le serveur TCP envoie un segment TCP ACK au dernier segment TCP d’un flux de données TCP dans le minuteur d’accusé de réception différé (également appelé minuteur ACK retardé). 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-Serveur> 1460 octets
Client-Serveur> 1460 octets
Serveur-Client> ACK
Client-Serveur> 1460 octets
Client-Serveur> 1460 octets
Serveur-Client> ACK
....
Client-Serveur> 1460 octets
Client-Serveur> 1460 octets
Serveur-Client> ACK-PUSH
Client-Serveur> 1296 octets
-> ACK retardé 200 ms

Cause

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

  • Le programme Windows Sockets utilise des sockets non bloquants.

  • Un seul appel d’envoi ou WSASend remplit la mémoire tampon d’envoi du socket sous-jacent entier.

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

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

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

    send(socket, pWrBuffer, 65536, 0);
    

    Dans ce scénario, chaque fois que le programme émet un appel d’envoi de 64 Ko de données, le programme retourne un code d’erreur SOCKET_ERROR si la mémoire tampon de socket de 32 Ko sous-jacente 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 select Windows Sockets pour case activée la status du socket. Dans ce scénario, la fonction select ne signale pas le socket comme accessible en écriture tant que le client n’a pas reçu le segment ACK TCP en attente. 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 accuse réception de tous les segments TCP avant que le client n’envoie le dernier segment TCP avec le bit push défini.

Solution de contournement

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

Méthode 1 : Utiliser des sockets bloquants

Ce problème se produit uniquement avec les sockets non bloquants. Lorsque vous utilisez un socket bloquant, ce problème ne se produit pas, car afd.sys gère la mémoire tampon de socket différemment. Pour plus d’informations sur la programmation de sockets bloquants et non bloquants, consultez la documentation du Kit de développement logiciel (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 la mémoire tampon d’envoi de socket, utilisez la fonction Windows Sockets getsockopt pour déterminer la taille actuelle de la mémoire tampon d’envoi du socket (SO_SNDBUF), puis utilisez la setsockopt fonction pour définir la taille de la mémoire tampon d’envoi du socket. Lorsque vous avez terminé, la valeur SO_SNDBUF doit être supérieure d’au moins 1 octet à 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 inférieure d’au moins 1 octet à la valeur SO_SNDBUF. 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

Importante

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 procédure de sauvegarde et de restauration du Registre, cliquez sur le numéro ci-dessous pour afficher l’article correspondant 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 accuser réception immédiate des segments TCP entrants. Cette solution de contournement fonctionne mieux dans un environnement qui a une grande base d’installation de clients 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 minuteur d’accusé de réception différé.

Sur un serveur qui exécute Windows 2000, procédez comme suit :

  1. Démarrez la Rédacteur du Registre (Regedit.exe).
  2. Recherchez la sous-clé de Registre suivante, puis cliquez dessus : 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, procédez comme suit :

  1. Démarrez l’Éditeur du Registre.
  2. Recherchez la sous-clé de Registre suivante, puis cliquez dessus : 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 affectez-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

Importante

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 sauvegarde et 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

Remarque

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, tapez regedit.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 affectez-lui la valeur 1.
  5. Quittez la Rédacteur du Registre.
  6. Redémarrez Windows pour que cette modification prenne effet.

État

Microsoft a confirmé l’existence de ce problème dans les produits Microsoft répertoriés dans la section « Produits concernés ».

References

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