簡體   English   中英

如何將來自 python cmd 的輸入發送到在同一解釋器中運行的高速公路 websocket 客戶端?

[英]How do I send input from python cmd to autobahn websocket client running in the same interpreter?

我正在嘗試通過 python 的 cmd 庫從交互式提示中獲取輸入,並將輸入傳遞給高速公路 websocket 客戶端以發送到 websocket 服務器。 cmd 循環和高速公路 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:

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:

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:

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 創建一個新的未連接協議實例,然后嘗試使用它,就像它已連接一樣:

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

具體來說,這會創建您的協議 class 的新實例:

        m = MyClientProtocol()

這會嘗試像連接一樣使用它:

        reactor.callFromThread(m.sendTask, payload)

稍后,您將擁有將協議實際連接到某些東西的代碼:

        connectWS(connectStatement, contextFactory)

但是,此代碼未以任何有用的方式連接到您的命令 class。

您需要使用調用connectWS產生的連接,而不是創建新的MyClientProtocol實例。

很多方法可以讓你權衡取舍。 一種很容易解釋的方法是使用在 websocket 代碼和命令解釋器代碼之間共享的可變 state。

例如, 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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM