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. P>
peut-il être résolu par sondage () ou sélectionnez.select ()? P>
5 Réponses :
Si vous souhaitez débloquer un UDP lu depuis un autre fil, envoyez-le un datagramme! p>
rgds, Martin P>
C'est génial si (addr, port) code> est unique. Que diriez-vous si
réeusaddr code> est activé et plusieurs processus écoutent?
Implémentez une commande Quitter sur les sockets de serveur et de client. Devrait fonctionner quelque chose comme ceci:
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 une suite de test: p> 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> Tout d'abord, nous modifions après cela, nous pouvons remplacer Retour '' ' code> par La version pratiquée peut être la suivante: p> 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.
point de départ h2>
interruptable_socket.py code> p>
test_interruptable_socket.py code> p>
évolution h2>
socket.recv () code>, étendre l'interruption à d'autres
recv code> Les méthodes deviennent très simples li>
test_interrupt_async () code> Pour vérifier l'exception à la place Message vide: p>
Soulevez l'interrupteXception code> et les tests passent à nouveau. 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())
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 CODE> S. / Testez). De plus, si la sous-classe
socket code> Je devrais prendre en charge le délai d'attente de socket dans tous les méthodes
RECV code> 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 code> 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 code> dans votre objet Pourquoi faire une sous-classe qui prend soin de tout
socket code> 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.
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: p>
Un appel à Fermer () Code> Seul I> le faire à moins que le socket FD ait été hérité.
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> et bien sûr, dans le thread / boucle effectuant la rétention: P> try:
while True:
myUdpSocket.recv(...)
except KeyboardInterrupt:
pass
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.