Un socket peut-il être fermé à partir d’un autre thread lorsqu’un envoi / enregistrement sur le même socket est en cours?

Un socket peut-il être fermé à partir d’un autre thread lorsqu’un envoi / enregistrement sur le même socket est en cours?

Supposons qu’un thread bloque l’appel recv et qu’un autre thread ferme le même socket. Le thread de l’appel recv le saura-t-il et sortira-t-il en toute sécurité?

Je voudrais savoir si le comportement sera différent entre différents systèmes d’exploitation / plates-formes. Si oui, comment va-t-il se comporter sous Solaris?

    Je ne connais pas la mise en œuvre de la stack réseau Solaris, mais je vais exposer ma théorie et expliquer pourquoi elle devrait être sûre.

    • Le fil A entre dans un appel système bloquant, disons read(2) , pour ce socket donné. Il n’y a pas de données dans le tampon de réception du socket, donc le thread A est retiré du processeur et mis en queue pour ce socket. Aucun événement de stack réseau n’est initié ici, l’état de la connexion (en supposant que TCP) n’a pas changé.
    • Le fil B émet à close(2) sur le socket. Alors que la structure du socket du kernel doit être verrouillée pendant que le thread B y accède, aucun autre thread ne détient ce verrou (le thread A a relâché le verrou quand il a été mis en veille). En supposant qu’il n’y ait pas de données en attente dans le tampon d’envoi de la socket, un paquet FIN est envoyé et la connexion entre dans l’état FIN WAIT 1 (encore une fois, je suppose que TCP est ici, voir le diagramme d’état de la connexion ).
    • J’imagine que le changement d’état d’une connexion de socket générerait un réveil pour tous les threads bloqués sur un socket donné. C’est-à-dire que le thread A entrerait dans un état exécutable et découvrirait que la connexion est en train de se fermer. L’attente peut être ré-entrée si l’autre partie n’a pas envoyé sa propre FIN ou l’appel système devrait revenir avec eof sinon.

    Dans tous les cas, les structures internes du kernel seront protégées contre les access concurrents inappropriés. Cela ne veut pas dire que c’est une bonne idée de faire des E / S de socket à partir de plusieurs threads. Je vous conseillerais de vous pencher sur les sockets non bloquants, les machines à états et les frameworks tels que libevent .

    Sous Linux, fermer un socket ne réveillera pas recv() . En outre, comme @jxh dit:

    Si un thread est bloqué sur recv () ou send () lorsque le socket est fermé par un autre thread, le thread bloqué recevra une erreur. Cependant, il est difficile de détecter l’action corrective correcte après avoir reçu l’erreur. Cela est dû au fait que le numéro de descripteur de fichier associé au socket a peut-être été récupéré par un autre thread et que le thread bloqué a maintenant été réveillé après une erreur pour un socket “valide”. Dans un tel cas, le thread réveillé ne devrait pas appeler close () lui-même.

    Le thread réveillé aura besoin d’un moyen de différencier si l’erreur a été générée par la connexion (par exemple, une erreur de réseau) qui l’oblige à appeler close () ou si l’erreur a été générée par un autre thread ayant appelé close (). , auquel cas il devrait simplement se tromper sans rien faire d’autre chose.

    Le meilleur moyen d’éviter les deux problèmes est donc d’appeler shutdown() au lieu de close() . shutdown() rendra le descripteur de fichier toujours disponible, donc ne sera pas alloué par un autre descripteur, réveillera également recv() avec une erreur et le thread avec l’appel recv() pourra fermer le socket normalement, comme un erreur normale s’est produite.

    Pour moi, le socket shutdown () d’un autre thread fait le travail sous Linux

    Si un thread est bloqué sur recv() ou send() lorsque le socket est fermé par un autre thread, le thread bloqué recevra une erreur. Cependant, il est difficile de détecter l’action corrective correcte après avoir reçu l’erreur. Cela est dû au fait que le numéro de descripteur de fichier associé au socket a peut-être été récupéré par un autre thread et que le thread bloqué a maintenant été réveillé après une erreur pour un socket “valide”. Dans un tel cas, le thread réveillé ne devrait pas appeler close() lui-même.

    Le thread réveillé aura besoin d’un moyen de différencier si l’erreur a été générée par la connexion (par exemple, une erreur de réseau) qui l’oblige à appeler close() ou si l’erreur a été générée par un autre thread ayant appelé close() . , auquel cas il devrait simplement se tromper sans rien faire d’autre chose.

    Oui, vous pouvez fermer le socket à partir d’un autre thread. Tous les threads bloqués / occupés qui utilisent ce socket signaleront une erreur appropriée.