2017-09-16 3 views
1

J'ai une application similaire à une écriture Chat en python qui a l'intention de faire les choses suivantes:Comment arrêter un client websocket sans arrêt réacteur

  1. Une invite pour l'utilisateur d'entrer l'adresse du serveur websocket.
  2. Créez ensuite un client Websocket qui se connecte au serveur et envoie/reçoit des messages. Désactive la possibilité de créer un client websocket.
  3. Après avoir reçu "fermer" du serveur (PAS un cadre proche), le client devrait abandonner la connexion et réactiver l'application pour créer un client. Revenez à 1.
  4. Si l'utilisateur quitte l'application, il quitte le client websocket s'il y en a un en cours d'exécution.

Mon approche pour cela consiste à utiliser un thread principal pour gérer les entrées utilisateur. Lorsque l'utilisateur clique, un thread est créé pour WebSocketClient en utilisant le module torsadé d'AutoBahn et lui transmet une file d'attente. Vérifiez si le réacteur fonctionne ou non et démarrez-le si ce n'est pas le cas. Écrasement sur la méthode de message pour mettre un drapeau de fermeture dans la file d'attente lors de la fermeture. Le thread principal sera occupé à vérifier la file d'attente jusqu'à la réception du drapeau et retournera au début. Le code ressemble à suivre.

Fil principal.

def main_thread(): 
    while True: 
     text = raw_input("Input server url or exit") 
     if text == "exit": 
      if myreactor: 
       myreactor.stop() 
      break 
     msgq = Queue.Queue() 
     threading.Thread(target=wsthread, args=(text, msgq)).start() 

     is_close = False 
     while True: 
      if msgq.empty() is False: 
       msg = msgq.get() 
       if msg == "close": 
        is_close = True 
       else: 
        print msg 
       if is_close: 
        break 
     print 'Websocket client closed!' 

Usine et protocole.

class MyProtocol(WebSocketClientProtocol): 
    def onMessage(self, payload, isBinary): 
     msg = payload.decode('utf-8') 
     self.Factory.q.put(msg) 
     if msg == 'close': 
      self.dropConnection(abort=True) 

class WebSocketClientFactoryWithQ(WebSocketClientFactory): 
    def __init__(self, *args, **kwargs): 
     self.queue = kwargs.pop('queue', None) 
     WebSocketClientFactory.__init__(self, *args, **kwargs) 

Processus client.

def wsthread(url, q): 
    factory = WebSocketClientFactoryWithQ(url=url, queue=q) 
    factory.protocol = MyProtocol 
    connectWS(Factory) 
    if myreactor is None: 
     myreactor = reactor 
     reactor.run() 
    print 'Done' 

Maintenant, j'ai un problème. Il semble que mon thread client ne s'arrête jamais. Même si je reçois "close", il semble toujours en cours d'exécution et chaque fois que j'essaie de recréer un nouveau client, cela crée un nouveau thread. Je comprends que le premier thread ne s'arrêtera pas puisque reactor.run() fonctionnera pour toujours, mais à partir du 2ème thread et sur, il devrait être non-bloquant puisque je ne le lance plus. Comment puis-je changer cela?

EDIT:

Je finis par le résoudre avec

  1. Ajout stopFactory() après la déconnexion.
  2. Effectuez des fonctions de protocole avec reactor.callFromThread().
  3. Démarrez le réacteur dans le premier thread et placez les clients dans d'autres threads et utilisez reactor.callInThread() pour les créer.
+0

Le serveur peut fermer la socket client, souhaitez-vous gérer toutes les erreurs client sur le serveur? – dsgdfg

+0

@dsgdfg Le fait est que j'essaie de dupliquer une application existante, mais je ne possède pas le client ou le serveur, donc je n'ai pas le code. De ce que je sais en utilisant wireshark, je peux voir le serveur envoie le message "fermer" et le client répond un cadre proche avec le code 1001 et quitte juste. Je veux faire la même chose mais je ne sais pas comment fermer le client et laisser le fil revenir afin que je puisse faire d'autres travaux. – vance46

Répondre

0

Votre main_thread crée de nouveaux threads en cours d'exécution wsthread. wsthread utilise des API Twisted. Le premierwsthread devient le fil du réacteur. Tous les threads suivants sont différents et il n'est pas défini ce qui se passe si vous utilisez une API Twisted.

Vous devriez presque certainement supprimer l'utilisation des threads de votre application. Pour traiter les entrées de la console dans une application Twisted, jetez un oeil à twisted.conch.stdio (pas la partie la mieux documentée de Twisted, hélas, mais juste ce que vous voulez).

+0

Merci de l'avoir signalé! Je pense que cela pourrait être le problème. Je comprends que l'utilisation d'un seul thread peut mieux s'adapter à Twisted, mais j'ai besoin de faire quelque chose -> démarrer client websocket -> faire quelque chose en fonction du résultat, répétez. Pour autant que je sache, j'ai besoin de faire le réacteur.run() pour démarrer le client websocket, mais il bloquera pour toujours. Y at-il une meilleure façon de faire ci-dessus sans utiliser de fil? J'ai aussi un fil d'interface graphique que je ne sais pas s'il est bon d'aller sans multithreading. – vance46

+0

Twisted a divers éléments d'intégration de l'interface graphique. Selon le toolkit, vous pouvez trouver un moyen d'exécuter une interface graphique et Twisted dans un seul thread. Aussi, si vous êtes familier avec la programmation GUI (sans fils supplémentaires, de toute façon) alors beaucoup de ces idées s'appliquent également à Twisted. Par exemple, vous n'écrivez pas une boucle 'while while:' dans Tkinter ... vous utilisez l'API 'after_idle'. De même, Twisted a des API pour vous permettre de programmer plus de code pour l'exécuter plus tard afin de vous éviter d'avoir une boucle qui bloque le thread du réacteur (et évite d'être bloqué par le fait que 'reactor.run' bloque). –