我如何将python cmd的输入发送到在同一解释器中运行的autobahn websocket客户端?

问题描述 投票:0回答:1

我正在尝试通过python的cmd库从交互式提示中获取输入,并将输入传递给autobahn websocket client以发送到Websocket服务器。 cmd循环和高速公路websocket客户端循环在同一解释器中运行。我正在尝试使用crochet来完成这项工作。 websocket客户端成功连接到服务器,但是当我在cmd提示符下输入某些内容以调用sendMessage时,出现了本文底部显示的异常。任何关于我可能陷入困境的指导将不胜感激。如果有更好的方法来完成我想做的事情,我会不知所措。

这些是相关的导入和设置:

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

这是将输入发送到websocket客户端的cmd类:

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

这就是我调用websocket客户端和cmd循环的方式:

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'
python python-3.x websocket twisted autobahn
1个回答
0
投票

您的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产生的连接。

许多种方式,您可以进行权衡取舍。一种容易解释的方法是使用websocket代码和命令解释器代码之间共享的可变状态。

例如,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)
© www.soinside.com 2019 - 2024. All rights reserved.