Passer au contenu principal

Avant la version 17.1 de VOS, le protocole STCP n'acceptait une demande de connexion TCP que si l'application serveur avait appelé la fonction accept. Les demandes de connexion étaient placées dans une file d'attente, mais aucune réponse n'était envoyée tant qu'un appel accept n'avait pas retiré la demande de cette file. Une fois la file d'attente pleine, le protocole STCP envoyait une commande de réinitialisation en réponse à une demande de connexion.

À partir de la version 17.1, le fonctionnement des connexions TCP a changé. Désormais, lorsqu'une demande de connexion arrive, en supposant que la file d'attente des demandes en attente n'est pas pleine, la pile STCP envoie immédiatement une réponse acceptant la connexion. La connexion est ensuite placée dans la file d'attente des demandes en attente. Une fois que cette file d'attente est pleine, les demandes de connexion suivantes ne reçoivent plus de réponse. Lorsque l'application serveur appelle la fonction accept, la connexion en tête de la file d'attente des demandes en attente est supprimée. Ce comportement est désormais similaire à celui des piles TCP sur la plupart des autres systèmes d'exploitation.

Dans des conditions normales, ce changement de dynamique n'entraînera aucune modification notable dans le fonctionnement des applications serveur ou client. Toutefois, si l'application serveur ralentit ou si les connexions arrivent plus rapidement que prévu, celles-ci peuvent rester dans la file d'attente des requêtes en attente pendant un certain temps. Dans ces circonstances, on peut observer certains comportements particuliers.

Le tableau suivant résume ce qui se passe lorsqu’une demande de connexion est placée dans la file d’attente des requêtes en attente, en fonction des actions du client. Le client peut ne rien faire et attendre que l’application serveur envoie des données, il peut envoyer des données et/ou fermer la connexion à l’aide d’un indicateur FIN (fin) (fermeture normale) ou d’un indicateur RST (réinitialisation) (fermeture anormale). Les quatre premières colonnes se comportent comme prévu. L'application serveur appelle accept pour récupérer un socket dans la file d'attente des requêtes en attente, puis appelle recv et soit se bloque s'il n'y a pas de données, soit récupère des données et/ou une indication de fin de fichier selon le contenu du socket. Ce sont les quatre dernières colonnes qui méritent d'être examinées de plus près.

La prise est vide Données dans le socket FIN dans la prise Données dans le socket/

FIN dans la prise

Données dans le socket/

FIN dans la prise/

La connexion client a été interrompue

FIN dans la prise/

La connexion client a été interrompue

Données dans le socket/

Réinitialisation envoyée

Réinitialiser l'envoi
Accepter Renvoie un socket Renvoie un socket Renvoie un socket Renvoie un socket Renvoie un socket Renvoie un socket Se suspend Se suspend
Première réception Se suspend Renvoie des données Renvoie EOF Renvoie des données Renvoie une erreur de réinitialisation Renvoie EOF
Deuxième réception Se suspend Renvoie EOF

 

Données présentes dans le socket / FIN dans le socket / Disparition du socket client
Dans ce scénario, le client a envoyé des données et a fermé la connexion. La version 17.1 de STCP accusera réception de toutes les données envoyées, mais n'accusera pas réception du FIN tant que les données de la file d'attente de réception n'auront pas été transmises à l'application. Par conséquent, le FIN du client sera retransmis jusqu'à l'expiration du délai de retransmission du client. Une application cliente bien conçue attendra que l'application serveur ferme sa partie de la connexion ; ainsi, lorsque le délai d'attente du FIN expirera, le client sera averti d'une erreur. Cependant, si l'application cliente ferme simplement le socket sans attendre de signal du serveur, elle ne saura pas que les données qu'elle a envoyées ont pu être perdues.

Lorsque l'application serveur appelle la fonction `accept` pour un paquet d'accusé de réception en double, elle envoie un accusé de réception pour les données, mais pas pour le paquet FIN. Comme le client a fermé son socket, il répond par un paquet `reset`. Ce paquet `reset` libère le socket côté serveur. L'application serveur recevra une erreur de réinitialisation lors du premier ou du deuxième appel à recv, en fonction du délai entre l'appel à accept et le premier appel à recv. Si ce délai est plus court que le temps nécessaire pour recevoir et traiter le paquet de réinitialisation du client, l'application serveur verra les données. Dans les deux cas, l'application serveur saura qu'une connexion a été établie et qu'elle a été interrompue.

FIN dans le socket / Le socket client a disparu
Ce scénario diffère du précédent en ce qu'il n'y a pas de données dans le socket. En l'absence de données, le FIN est acquitté. La fonction `accept` renvoie le socket et la fonction `recv` renvoie un EOF indiquant qu'elle a reçu le FIN. Toute tentative d'écriture dans le socket, y compris sa fermeture, entraînera le renvoi d'une erreur de réinitialisation, car le client ne dispose plus du socket correspondant.

Données dans le socket / Réinitialisation envoyée
Ici, le client envoie des données, puis une réinitialisation. Il peut y avoir plusieurs raisons à cette réinitialisation ; l'une des plus courantes est que l'application ferme le socket et que la pile TCP envoie une réinitialisation soit immédiatement, soit après avoir échoué à obtenir un accusé de réception pour le paquet FIN. Ce scénario est similaire au premier, mais la pile TCP du client envoie une réinitialisation avant de fermer son socket, au lieu de simplement fermer le socket. Une fois encore, une application client bien écrite considérera cela comme une erreur ; une application moins bien écrite ne le considérera pas comme une erreur.

La connexion au serveur est interrompue et toutes les données sont supprimées dès la réception de la commande de réinitialisation ; l'application serveur ne détecte donc même pas qu'une connexion a été établie ; elle reste simplement en attente d'une nouvelle connexion.

Réinitialisation envoyée à
Une fois encore, la réinitialisation supprime effectivement le socket de la file d'attente des requêtes en attente, de sorte que la fonction `accept` ne le voit pas et attend une autre demande de connexion.

 

Qu'est-ce que tout cela signifie concrètement ? Premièrement, les applications clientes peuvent désormais bénéficier d'une réponse de connexion plus rapide, mais elles devront peut-être attendre plus longtemps avant de recevoir une bannière ou un message au niveau de l'application. Il faudra peut-être augmenter les délais d'expiration prévus pour cette bannière ou ce message. Deuxièmement, les applications serveur doivent être prêtes à gérer une erreur provenant de l'appel `recv` immédiatement après avoir effectué un `accept`. Cela a toujours été possible, mais la probabilité est désormais plus élevée. Enfin, les applications clientes qui envoient des données au serveur et n'attendent aucun message en retour doivent être programmées pour vérifier que l'application serveur a correctement fermé la connexion, sous peine de perdre des données sans s'en rendre compte. Là encore, cette possibilité a toujours existé et n'est pas propre au protocole STCP, mais la probabilité est désormais légèrement plus élevée.