Débloquer recvfrom lorsque le socket est fermé

Disons que je commence un fil pour recevoir sur un port. L’appel de la prise bloquera sur recvfrom. Puis, dans un autre thread, je ferme le socket.

Sous Windows, cela débloquera recvfrom et l’exécution de mon thread se terminera.

Sous Linux, cela ne débloque pas recvfrom et, par conséquent, mon thread est inactif, et son exécution ne se termine pas.

Quelqu’un peut-il m’aider avec ce qui se passe sous Linux? Lorsque le socket est fermé, je veux que recvfrom se débloque

Je continue à lire sur l’utilisation de select (), mais je ne sais pas comment l’utiliser pour mon cas spécifique.

Appelez shutdown(sock, SHUT_RDWR) sur le socket, puis attendez que le thread se termine. (ie pthread_join ).

Vous penseriez que close() débloquerait recvfrom() , mais ce n’est pas le cas sous Linux.

Voici un exemple simple d’utilisation de select () pour résoudre ce problème:

 // Note: untested code, may contain typos or bugs static volatile bool _threadGoAway = false; void MyThread(void *) { int fd = (your socket fd); while(1) { struct timeval timeout = {1, 0}; // make select() return once per second fd_set readSet; FD_ZERO(&readSet); FD_SET(fd, &readSet); if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0) { if (_threadGoAway) { printf("MyThread: main thread wants me to scram, bye bye!\n"); return; } else if (FD_ISSET(fd, &readSet)) { char buf[1024]; int numBytes = recvfrom(fd, buf, sizeof(buf), 0); [...handle the received bytes here...] } } else perror("select"); } } // To be called by the main thread at shutdown time void MakeTheReadThreadGoAway() { _threadGoAway = true; (void) pthread_join(_thread, NULL); // may block for up to one second } 

Une méthode plus élégante consisterait à éviter d’utiliser la fonctionnalité de délai d’attente de select et à créer une paire de sockets (à l’aide de socketpair ()) et de laisser le thread principal envoyer un octet à l’extrémité de la paire de sockets lorsqu’il souhaite utiliser le thread d’E / S. s’en aller et laisser le thread d’E / S sortir lorsqu’il reçoit un octet sur son socket à l’autre extrémité du socketpair. Je vais laisser cela comme un exercice pour le lecteur cependant. 🙂

Il est également souvent utile de définir le socket en mode non bloquant afin d’éviter le risque (petit mais non nul) que l’appel de recvfrom () bloque même après que select () a indiqué que le socket est prêt à la lecture. , comme décrit ici . Mais le mode de blocage pourrait être “assez bon” pour votre objective.

Pas une réponse, mais la page de manuel Linux close contient la citation intéressante:

Il est probablement déconseillé de fermer les descripteurs de fichier alors qu’ils peuvent être utilisés par des appels système dans d’autres threads du même processus. Dans la mesure où un descripteur de fichier peut être réutilisé, certaines conditions de concurrence obscures peuvent provoquer des effets secondaires non souhaités.

Vous demandez l’impossible. Il est tout simplement impossible pour le thread qui appelle close de savoir que l’autre thread est bloqué dans recvfrom . Essayez d’écrire du code garantissant que cela se produit, vous constaterez que c’est impossible.

Quoi que vous fassiez, il sera toujours possible pour l’appel de se close de la course avec l’appel de recvfrom . L’appel à close change le nom du descripteur de socket, il peut donc changer le sens sémantique de l’appel à recvfrom .

Le thread qui entre recvfrom ne peut en aucun cas signaler au thread qui appelle close qu’il est bloqué (au lieu d’être sur le sharepoint bloquer ou de simplement entrer dans l’appel système). Il n’ya donc littéralement aucun moyen de s’assurer que le comportement de close et de recvfrom est prévisible.

Considérer ce qui suit:

  1. Un thread est sur le point d’appeler recvfrom , mais il est recvfrom par d’autres recvfrom que le système doit effectuer.
  2. Plus tard, les appels du fil se close .
  3. Un thread démarré par la bibliothèque d’E / S du système appelle socket et obtient le même descripteur que celui que vous close d.
  4. Finalement, le thread appelle recvfrom , et maintenant il reçoit du socket l’ouverture de la bibliothèque.

Oops.

Ne jamais faire quelque chose, même à distance, comme ça. Une ressource ne doit pas être libérée pendant qu’un autre thread l’utilise ou pourrait l’utiliser. Période.

Lorsque le socket est fermé, je veux que recvfrom se débloque

recvfrom () est une fonction spécifique aux sockets UDP sur Python. Voici un bref résumé de la façon dont j’ai résolu le problème en utilisant une idée appelée “interrogation” (essayez également de lancer le programme, les instructions d’impression vous donneront une idée précise de ce qui se passe):

 import socket import threading import signal import time # Custom class to create a socket, close the socket, and poll the socket class ServerSocket(): def __init__(self, addresses): # "Standard" way to create and preapare a working socket self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.bind(addresses) def poll(self): self.socket.settimeout(2) modifiedMsg, senderAddress = self.socket.recvfrom(1024) def close(self): self.socket.close() class ServiceExit(Exception): """ Custom exception which is used to sortinggger the clean exit of all running threads and the main program. """ pass def service_shutdown(signum, frame): raise ServiceExit # Custom class to create a UDP server on a separate thread. # This server will know to close the blocking UDP socket when the user # of the main program signals termination via typing CTRL-C into the terminal class Server(threading.Thread): def __init__(self, addresses): threading.Thread.__init__(self) self.mysocket = ServerSocket(addresses) # This flag below will help us determine when to stop the "run" loop below # The while loop below is where interrupt the blocking recvfrom() call by # timing out every 2 seconds and checking to see if the flag has been set # to discontinue the while loop self.shutdown_flag = threading.Event() def run(self): while not self.shutdown_flag.is_set(): try: print('socket blocking') self.mysocket.poll() except socket.timeout: print('socket unblocked') pass # as a final step, we close the socket self.mysocket.close() print('socket closed') def main(): # assign the methods that will be called when our main program receives a SIGTERM or SIGINT signal # You can send this main problem such a signal by typing CTRL-C after you run this program signal.signal(signal.SIGTERM, service_shutdown) signal.signal(signal.SIGINT, service_shutdown) # Start the server thread that will eventually block on recvfrom() try: print('starting udp server thread') udp_server = Server(('localhost', 5000)) udp_server.start() while True: time.sleep(0.5) # This server will accept UDP packets on the local host at port 5000 # Feel free to change these settings to fit your needs except ServiceExit: print('shutting down server thread') udp_server.shutdown_flag.set() udp_server.join() print('server thread shut down') if __name__ == '__main__': main()