Aucune des réponses existantes ne dit aux gens comment shutdown
et close
fonctionne au niveau du protocole TCP, il vaut donc la peine d'ajouter cela.
Une connexion TCP standard est interrompue par une finalisation à 4 voies:
- Une fois qu'un participant n'a plus de données à envoyer, il envoie un paquet FIN à l'autre
- L'autre partie renvoie un ACK pour la FIN.
- Lorsque l'autre partie a également terminé le transfert de données, elle envoie un autre paquet FIN
- Le participant initial retourne un ACK et finalise le transfert.
Cependant, il existe une autre façon "émergente" de fermer une connexion TCP:
- Un participant envoie un paquet RST et abandonne la connexion
- L'autre côté reçoit une TVD, puis abandonne également la connexion
Dans mon test avec Wireshark, avec les options de socket par défaut, shutdown
envoie un paquet FIN à l'autre extrémité, mais c'est tout ce qu'il fait. Jusqu'à ce que l'autre partie vous envoie le paquet FIN, vous pouvez toujours recevoir des données. Une fois que cela s'est produit, vous Receive
obtiendrez un résultat de taille 0. Donc, si vous êtes le premier à arrêter l'envoi, vous devez fermer le socket une fois que vous avez fini de recevoir les données.
D'un autre côté, si vous appelez close
alors que la connexion est toujours active (l'autre côté est toujours actif et vous pouvez également avoir des données non envoyées dans la mémoire tampon du système), un paquet RST sera envoyé à l'autre côté. C'est bon pour les erreurs. Par exemple, si vous pensez que l'autre partie a fourni des données erronées ou a refusé de fournir des données (attaque DOS?), Vous pouvez fermer le socket immédiatement.
Mon opinion sur les règles serait:
- Réfléchissez
shutdown
avant close
si possible
- Si vous avez fini de recevoir (données de taille 0 reçues) avant de décider d'arrêter, fermez la connexion une fois le dernier envoi (le cas échéant) terminé.
- Si vous souhaitez fermer la connexion normalement, fermez la connexion (avec SHUT_WR, et si vous ne vous souciez pas de recevoir des données après ce point, avec SHUT_RD également), et attendez jusqu'à ce que vous receviez une donnée de taille 0, puis fermez le prise.
- Dans tous les cas, si une autre erreur s'est produite (timeout par exemple), fermez simplement le socket.
Implémentations idéales pour SHUT_RD et SHUT_WR
Les éléments suivants n'ont pas été testés, faites confiance à vos risques et périls. Cependant, je pense que c'est une façon raisonnable et pratique de faire les choses.
Si la pile TCP reçoit un arrêt avec SHUT_RD uniquement, elle doit marquer cette connexion comme plus de données attendues. Toutes les read
demandes en attente et suivantes (quel que soit le thread dans lequel elles se trouvent) seront alors renvoyées avec un résultat de taille nulle. Cependant, la connexion est toujours active et utilisable - vous pouvez toujours recevoir des données OOB, par exemple. En outre, le système d'exploitation supprimera toutes les données qu'il reçoit pour cette connexion. Mais c'est tout, aucun colis ne sera envoyé de l'autre côté.
Si la pile TCP reçoit un arrêt avec SHUT_WR uniquement, elle doit marquer cette connexion car plus aucune donnée ne peut être envoyée. Toutes les demandes d'écriture en attente seront terminées, mais les demandes d'écriture suivantes échoueront. De plus, un paquet FIN sera envoyé à un autre côté pour les informer que nous n'avons plus de données à envoyer.