7
votes

Comment puis-je abandonner une socket.Recvfrom () d'un autre fil à Python?

Ceci ressemble à un duplicata de Comment puis-je abandonner une socket.Recv () d'un autre fil dans Python , mais ce n'est pas le cas, car je veux abandonner RecvFrom () dans un fil, qui est UDP, pas TCP.

peut-il être résolu par sondage () ou sélectionnez.select ()?


6 commentaires

UDP ou TCP n'a rien à voir avec le fileté ou non.


Vous pouvez utiliser l'ancienne astuce C: Créez un faux tuyau et utilisez Sélectionner sur le faux et la prise. Lorsque vous voulez arrêter de simples, envoyez un message au faux .... Si je trouve quelque temps, je déposerai une réponse avec tous les détails.


@qarma Jetez un coup d'œil à mon commentaire et laissez-moi savoir si vous êtes intéressé par ce type de solution.


@ Micheled'amico oui c'est une solution valide


@qarma je vais l'écrire ceci nous


Dans mon expérience, sélectionnez.select () a travaillé brillamment pour cela.


5 Réponses :


5
votes

Si vous souhaitez débloquer un UDP lu depuis un autre fil, envoyez-le un datagramme!

rgds, Martin


1 commentaires

C'est génial si (addr, port) est unique. Que diriez-vous si réeusaddr est activé et plusieurs processus écoutent?



0
votes

Implémentez une commande Quitter sur les sockets de serveur et de client. Devrait fonctionner quelque chose comme ceci: xxx


0 commentaires

3
votes

Un bon moyen de gérer ce type d'interruption asynchrone est l'ancien tour de pipe C. Vous pouvez créer un tuyau et utiliser SELECT CODE> / Sondage code> sur la prise et la conduite: maintenant lorsque vous souhaitez interrompre le récepteur, vous pouvez simplement envoyer un caractère au tuyau.

  • Avantages:
    • peut fonctionner à la fois pour UDP et TCP LI>
    • est protocole agnostique li> ul> li>
    • inconvénients:
      • Select / Sondage sur les tuyaux ne sont pas disponibles sous Windows, dans ce cas, vous devez le remplacer par une autre prise UDP utilisée comme notification tuyau em> li> li> ul> li> ul>

        point de départ h2>

        interruptable_socket.py code> p> xxx pré>

        une suite de test: p> test_interruptable_socket.py code> p> xxx pré>

        évolution h2>

        Ce code n'est qu'un point de départ. Pour le rendre plus générique, je vois que nous devrions résoudre les problèmes suivants: P>

        1. interface forte>: retour message vide dans l'affaire Interrupt n'est pas une bonne affaire, vaut mieux utiliser une exception pour le gérer li>
        2. Généralisation forte>: nous devrions avoir juste une fonction à appeler avant socket.recv () code>, étendre l'interruption à d'autres recv code> Les méthodes deviennent très simples li>
        3. portabilité forte>: Pour faire un port simple à Windows, nous devrions isoler la notification ASYNC dans un objet de choisir la bonne implémentation de notre système d'exploitation LI> ol>

          Tout d'abord, nous modifions test_interrupt_async () code> Pour vérifier l'exception à la place Message vide: p> xxx pré>

          après cela, nous pouvons remplacer Retour '' ' code> par Soulevez l'interrupteXception code> et les tests passent à nouveau. p>

          La version pratiquée peut être la suivante: p>

          interruptible_socket.py code> p>

          import os
          import socket
          import select
          
          
          class InterruptException(Exception):
              pass
          
          
          class InterruptableUdpSocketReceiver(object):
              def __init__(self, host, port):
                  self._host = host
                  self._port = port
                  self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                  self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                  self._async_interrupt = AsycInterrupt(self._socket)
          
              def bind(self):
                  self._socket.bind((self._host, self._port))
          
              def recv(self, buffersize, flags=0):
                  self._async_interrupt.wait_for_receive()
                  return self._socket.recv(buffersize, flags)
          
              def interrupt(self):
                  self._async_interrupt.interrupt()
          
          
          class AsycInterrupt(object):
              def __init__(self, descriptor):
                  self._read, self._write = os.pipe()
                  self._interrupted = False
                  self._descriptor = descriptor
          
              def interrupt(self):
                  self._interrupted = True
                  self._notify()
          
              def wait_for_receive(self):
                  if self._interrupted:
                      raise RuntimeError("Cannot be reused")
                  read, _w, errors = select.select([self._read, self._descriptor], [], [self._descriptor])
                  if self._descriptor not in read:
                      raise InterruptException
          
              def _notify(self):
                  os.write(self._write, "I".encode())
          


4 commentaires

La quantité de code est surchargée, pourquoi ne pas la prise de sous-classe directement?


Parce que dans cet exemple, je ne voulais pas m'occuper de toutes les méthodes et de remplacement de tous les 4 RECV S. / Testez). De plus, si la sous-classe socket Je devrais prendre en charge le délai d'attente de socket dans tous les méthodes RECV S. C'est un exemple et une suite de tests: remplacer le collaborateur par la sous-classe est simple (mais vous devez résoudre les problèmes ci-dessus). Mais avant, il est meilleur 1- Utilisez une exception au lieu de retourner le message vide, 2- Créez un décorateur pour vérifier et attendre le message entrant pour l'utiliser dans tous les 4 RECV S, 3- extraire un objet qui fait La notification asynchrone pour la remplacer par une implémentation UDP sous Windows.


@qarma Une dernière chose: le sous-classement est un outil puissant, mais il est parfois préférable d'utiliser un collaborateur car lorsque vous sous-classant un nouvel objet est l'objet parent et vous devez prendre soin de tous les comportements et responsabilités des parents. Utiliser Collaborator Simpler Suivez le principe de responsabilité unique et prenez votre code plus propre: si vous utilisez juste recv_from dans votre objet Pourquoi faire une sous-classe qui prend soin de tout socket méthodes? Lorsque vous avez besoin d'autres méthodes, vous pouvez envisager de le modifier dans une sous-classe, votre test vous aidera à le faire en sécurité.


Wow tu es prolifique! Bien que je préférerais la même API que la socket et la base de code beaucoup plus petite, votre réponse est la meilleure de là, Ergo +50 est la vôtre.



0
votes

La solution ici est de fermer de force la prise. Le problème est que la méthode de fonctionnement est spécifique au système d'exploitation et Python ne fait pas de bon travail d'abstrait de la manière de le faire ou des conséquences. Fondamentalement, vous devez faire une fermeture () suivie d'une fermeture () sur la prise. Sur les systèmes POSIX tels que Linux, l'arrêt est l'élément clé de la forçage de RECVFROM pour arrêter (un appel à fermer () seul ne le fera pas). Sous Windows, l'arrêt () n'affecte pas le RECVFROM et la fermeture () est l'élément clé. C'est exactement le comportement que vous verriez si vous impliez ce code dans C et à l'aide des prises de point de poix natives ou des prises Winsock, donc Python fournit une couche très mince sur ces appels.

sur les systèmes POSIX et Windows Cette séquence d'appels aboutit à un OsError étant soulevé. Cependant, l'emplacement de l'exception et les détails de celui-ci sont spécifiques au système d'exploitation. Sur les systèmes POSIX, l'exception est soulevée sur l'appel à l'arrêt () et la valeur errno de l'exception est définie sur 107 (le point final de transport n'est pas connecté). Sur les systèmes Windows, l'exception est soulevée sur l'appel à RECVFROM () et la valeur WinError de l'exception est définie sur 10038 (une opération a été tentée sur quelque chose qui n'est pas une prise). Cela signifie qu'il n'y a aucun moyen de le faire sur une manière OS-Agnositc, le code doit rendre compte à la fois pour le comportement et les erreurs de Windows et de POSIX. Voici un exemple simple que j'ai écrit: xxx


1 commentaires

Un appel à Fermer () Seul le faire à moins que le socket FD ait été hérité.



0
votes

Pour fermer correctement une prise TCP en Python, vous devez appeler Socket.Shutdown (ARG) avant d'appeler Socket.Close (). Voir la documentation de la prise Python, la partie sur l'arrêt de l'arrêt.

Si la prise est UDP, vous ne pouvez pas appeler Socket.Shutdown (...), cela constituerait une exception. Et appeler socket.close () seul, comme pour TCP, gardez le blocage des opérations bloquées. Fermer () seul ne les interrompez pas. P>

Beaucoup de solutions suggérées (pas toutes), ne fonctionnent pas ou sont considérées comme encombrantes car elles impliquent des bibliothèques tierces. Je n'ai pas testé le sondage () ou sélectionnez (). Ce qui fonctionne certainement, c'est ce qui suit: p>

Tout d'abord, créez un objet de thread officiel pour tout le fil exécute socket.Recv () et enregistrez la poignée. Deuxièmement, le signal d'importation. Le signal est une bibliothèque officielle, qui permet d'envoyer / réattribuer des signaux Linux / POSIX aux processus (lire sa documentation). Troisièmement, pour interrompre, en supposant que la poignée de votre thread est appelée UDPTHeadHandle: p> xxx pré>

et bien sûr, dans le thread / boucle effectuant la rétention: P>

try:
    while True:
       myUdpSocket.recv(...)
except KeyboardInterrupt:
    pass


0 commentaires