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



我正试图通过python的cmd库从交互式提示中获取输入,并将输入传递到高速公路websocket客户端,以发送到websocket服务器。cmd循环和autobahn websocket客户端循环在同一个解释器中运行。我正试着用钩针编织来使它发挥作用。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'

您的命令类创建一个新的、未连接的协议实例,然后尝试将其用作已连接的实例:

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)

相关内容

最新更新