Как отправить входные данные из python cmd клиенту autobahn websocket, работающему в том же интерпретаторе? - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь получить ввод из интерактивного приглашения через библиотеку cmd python и передать ввод клиенту websocket autobahn для отправки на сервер websocket. Cmd l oop и клиент веб-сокета autobahn l oop работают в одном и том же интерпретаторе. Я пытаюсь использовать вязание крючком , чтобы сделать эту работу. Клиент websocket успешно подключается к серверу, но когда я в командной строке вводю что-то для вызова sendMessage, я получаю исключение, показанное внизу этого поста. Любое руководство о том, где я, возможно, испортил бы, очень ценится. Если есть лучший подход к выполнению sh того, что я пытаюсь сделать, я весь в ушах.

Это соответствующие операции импорта и настройки:

from cmd import Cmd
from crochet import setup, run_in_reactor, wait_for, retrieve_result, TimeoutError

# Setup crochet before importing twisted
setup()

from twisted.internet import reactor, ssl
from twisted.python import log
from autobahn.twisted.websocket import WebSocketClientFactory, \
    WebSocketClientProtocol, \
    connectWS

Это Класс протокола клиента websocket:

class MyClientProtocol(WebSocketClientProtocol):

    def __init__(self, *args, **kwargs):
        super(MyClientProtocol, self).__init__(*args, **kwargs)

    def onConnect(self, response):
        print("Connected")

    def onMessage(self, payload, isBinary):
        if not isBinary:
            print('Message received: {}'.format(payload.decode('utf8')))

    def sendTask(self, payload):
        payload = json.dumps(payload, ensure_ascii = False).encode('utf8')
        self.sendMessage(payload)

Это класс фабрики клиента websocket:

class MyClientFactory(WebSocketClientFactory):

    def __init__(self, *args, **kwargs):
        super(MyClientFactory, self).__init__(*args, **kwargs)

    def buildFactory(self, uri, headers):
        factory = WebSocketClientFactory(uri, headers=headers)
        factory.protocol = MyClientProtocol
        return factory

Этот класс cmd отправляет входные данные клиенту websocket:

class mycmd(Cmd):
    def do_send(self, inp):
        payload = {'task': inp}
        m = MyClientProtocol()
        reactor.callFromThread(m.sendTask, payload)

Вот как я звоню клиенту websocket и cmd l oop:

if __name__ == '__main__':

    @run_in_reactor
    def start_connectWS():
        headers = {'header1': 'value1'}
        f = MyClientFactory()
        connectStatement = f.buildFactory(uri, headers)
        if connectStatement.isSecure:
            contextFactory = ssl.ClientContextFactory()
        else:
            contextFactory = None
        connectWS(connectStatement, contextFactory)

    start_connectWS()
    mycmd().cmdloop()

Это исключение:

Unhandled Error
Traceback (most recent call last):
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/tomd/project/lib/python3.7/site-packages/crochet/_eventloop.py", line 412, in <lambda>
    target=lambda: self._reactor.run(installSignalHandlers=False),
  File "/Users/tomd/project/lib/python3.7/site-packages/twisted/internet/base.py", line 1283, in run
    self.mainLoop()
  File "/Users/tomd/project/lib/python3.7/site-packages/twisted/internet/base.py", line 1292, in mainLoop
    self.runUntilCurrent()
--- <exception caught here> ---
  File "/Users/tomd/project/lib/python3.7/site-packages/twisted/internet/base.py", line 886, in runUntilCurrent
    f(*a, **kw)
  File "./client.py", line 62, in sendTask
    self.sendMessage(payload)
  File "/Users/tomd/project/lib/python3.7/site-packages/autobahn/websocket/protocol.py", line 2215, in sendMessage
    if self.state != WebSocketProtocol.STATE_OPEN:
builtins.AttributeError: 'MyClientProtocol' object has no attribute 'state'

1 Ответ

0 голосов
/ 07 апреля 2020

Ваш класс copmmand создает новый, неподключенный экземпляр протокола и затем пытается использовать его, как если бы он был подключен:

class mycmd(Cmd):
    def do_send(self, inp):
        payload = {'task': inp}
        m = MyClientProtocol()
        reactor.callFromThread(m.sendTask, payload)

В частности, это создает новый экземпляр класса протокола:

        m = MyClientProtocol()

И он пытается использовать его так, как если бы он был подключен:

        reactor.callFromThread(m.sendTask, payload)

Позже у вас есть код, который фактически соединяет протокол с чем-то:

        connectWS(connectStatement, contextFactory)

Однако это код не связан с вашим классом команд каким-либо полезным способом.

Вместо создания нового экземпляра MyClientProtocol вам необходимо использовать соединение, полученное в результате вызова connectWS.

. много способов, которыми вы могли бы go об этом, что различные компромиссы. Один из способов, который легко объяснить, - это использовать изменяемое состояние, совместно используемое кодом веб-сокета и кодом интерпретатора команд.

Например, MyClientProtocol.onConnect может установить себя в качестве атрибута на фабричном экземпляре и вашей команде Код строки может принять экземпляр фабрики в качестве аргумента, а затем прочитать подключенный экземпляр протокола из атрибута.

class MyClientProtocol(...):
    def onConnect(self, response):
        self.factory.connectedProtocol = self
    ...

class mycmd(Cmd):
    # ... __init__ that accepts factory and sets it on self

    def do_send(self, inp):
        payload = {'task': inp}
        m = self.factory.connectedProtocol
        if m is None:
            print("No connection")
        else:
            reactor.callFromThread(m.sendTask, payload)
...