我正在尝试通过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'
您的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)