web-dev-qa-db-fra.com

fermer vs socket d'arrêt?

En C, j’ai compris que si nous fermons un socket, cela signifie que le socket sera détruit et pourra être réutilisé plus tard.

Qu'en est-il de l'arrêt? La description dit qu'il ferme la moitié d'une connexion duplex à cette prise. Mais cette socket sera-t-elle détruite comme un appel système close

181
hdn

Ceci est a expliqué dans le guide de réseautage de Beej. shutdown est un moyen flexible de bloquer la communication dans un sens ou dans les deux. Lorsque le deuxième paramètre est SHUT_RDWR, il bloque l'envoi et la réception (comme close). Cependant, close est le moyen de détruire un socket.

Avec shutdown, vous pourrez toujours recevoir les données en attente de l'homologue déjà envoyé (merci à Joey Adams de l'avoir noté).

163
Matthew Flaschen

Aucune des réponses existantes n'indique aux gens comment shutdown et close fonctionnent au niveau du protocole TCP, il est donc intéressant de l'ajouter.

Une connexion standard TCP est terminée par une finalisation à 4 directions:

  1. Lorsqu'un participant n'a plus de données à envoyer, il envoie un paquet FIN à l'autre
  2. L'autre partie renvoie un ACK pour le FIN.
  3. Lorsque le correspondant a également terminé le transfert de données, il envoie un autre paquet FIN.
  4. Le participant initial renvoie un ACK et finalise le transfert.

Cependant, il existe un autre moyen "émergent" de fermer une connexion TCP:

  1. Un participant envoie un paquet RST et abandonne la connexion
  2. 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 que cela fait. Jusqu'à ce que votre correspondant vous envoie le paquet FIN, vous pouvez toujours recevoir des données. Une fois que cela est arrivé, votre Receive obtiendra un résultat de taille 0. Donc, si vous êtes le premier à fermer "envoyer", fermez le socket une fois la réception des données terminée.

D'autre part, si vous appelez close alors que la connexion est toujours active (l'autre côté est toujours actif et que des données non envoyées peuvent également figurer 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 de mauvaises données ou si elle a refusé de fournir des données (attaque DOS?), Vous pouvez immédiatement fermer le socket.

Mon opinion sur les règles serait:

  1. Considérez shutdown avant close lorsque cela est possible
  2. Si vous avez fini de recevoir (données de taille 0 reçues) avant de décider de fermer, fermez la connexion après le dernier envoi (le cas échéant).
  3. Si vous voulez fermer la connexion normalement, fermez-la (avec SHUT_WR, et si vous ne vous souciez plus de recevoir des données après ce point, avec SHUT_RD également), attendez de recevoir des données de taille 0, puis fermez le prise.
  4. Dans tous les cas, si une autre erreur survient (délai dépassé, 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, j'estime qu'il s'agit d'une façon raisonnable et pratique de faire les choses.

Si la pile TCP reçoit un arrêt uniquement avec SHUT_RD, elle doit marquer cette connexion comme aucune autre donnée n'est attendue. Toutes les demandes read en attente et suivantes (quel que soit le thread dans lequel elles se trouvent) seront alors renvoyées avec un résultat de taille zéro. Cependant, la connexion est toujours active et utilisable - vous pouvez toujours recevoir des données hors bande, 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 uniquement avec SHUT_WR, elle doit marquer cette connexion car aucune autre 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 l'informer que nous n'avons plus de données à envoyer.

110
Earth Engine

close() peut être évité si l'on utilise plutôt shutdown().

close() mettra fin aux deux directions sur une connexion TCP. Parfois, vous souhaitez indiquer à l'autre terminal que l'envoi de données est terminé mais que vous souhaitez tout de même recevoir des données.

close() décrémente le décompte de références des descripteurs (maintenu dans l’entrée de la table des fichiers et compte le nombre de descripteurs actuellement ouverts qui font référence à un fichier/socket) et ne ferme pas le socket/fichier si le descripteur n’est pas 0. Cela signifie que si vous , le nettoyage ne se produit qu’après que le compte de références soit tombé à 0. Avec shutdown(), on peut initier une séquence de fermeture normale TCP en ignorant le compte de références.

Les paramètres sont les suivants:

int shutdown(int s, int how); // s is socket descriptor

int how peut être:

SHUT_RD ou 0 Reçoit plus sont interdits

SHUT_WR ou 1 Les autres envois sont interdits

SHUT_RDWR ou 2 Les autres envois et les réceptions sont interdits

33
Milan

Cela peut être spécifique à la plate-forme, j'en doute, mais de toute façon, la meilleure explication que j'ai vue est ici sur cette page msdn où ils expliquent les options d'arrêt, de durée, de fermeture de socket et de connexion générale.

En résumé, utilisez shutdown pour envoyer une séquence d'arrêt au niveau TCP et utilisez close pour libérer les ressources utilisées par les structures de données de socket dans votre processus. Si vous n'avez pas émis de séquence d'arrêt explicite au moment où vous appelez fermer, une autre est lancée pour vous.

15
Len Holgate

"shutdown () ne ferme pas le descripteur de fichier, il change simplement sa facilité d'utilisation. Pour libérer un descripteur de socket, vous devez utiliser close ()." 1

7

J'ai aussi eu du succès sous Linux en utilisant shutdown() d'un pthread pour forcer un autre pthread actuellement bloqué dans connect() à abandonner tôt. 

Sous d'autres OS (au moins OSX), j'ai trouvé qu'appeler close() suffisait pour que connect() échoue.

7
Toby

Fermer

Lorsque vous avez fini d’utiliser un socket, vous pouvez simplement fermer son descripteur de fichier avec close; S'il y a encore des données en attente de transmission sur la connexion, normalement close tente de terminer cette transmission. Vous pouvez contrôler ce comportement en utilisant l'option de socket SO_LINGER pour spécifier un délai d'attente. voir Options de socket. 

Fermer

Vous pouvez également arrêter uniquement la réception ou l'émission sur une connexion en appelant l'arrêt.

La fonction d'arrêt met fin à la connexion du socket. Son argument how spécifie quelle action effectuer: 0 Arrête de recevoir des données pour ce socket. Si d'autres données arrivent, rejetez-les. 1 Arrêtez d’essayer de transmettre des données à partir de ce socket. Supprimer toutes les données en attente d'envoi. Arrêtez de rechercher un accusé de réception des données déjà envoyées; ne le retransmettez pas s'il est perdu. 2 Arrêtez la réception et la transmission. 

La valeur de retour est 0 en cas de succès et -1 en cas d'échec.

3
Neha Agrawal

dans mon test.

close enverra un paquet fin et détruira fd immédiatement lorsque socket n’est pas partagé avec d’autres processus

shutdown SHUT_RD , le processus peut toujours récupérer les données du socket, mais recv retournera 0 si le tampon TCP est vide.Après que l'hôte envoie plus de données, recv retournera les données à nouveau.

shutdown SHUT_WR enverra un paquet final pour indiquer que les autres envois sont interdits. l'homologue peut recevoir des données, mais il recevra 0 si son tampon TCP est vide

shutdown SHUT_RDWR (égal à utiliser à la fois SHUT_RD et SHUT_WR ) enverra le premier paquet si l’homologue envoie plus de données.

1
simpx