使用Twisted / Autobahn Websockets编写“交互式”客户端

时间:2013-09-19 15:55:35

标签: python websocket twisted autobahn

也许我在Twisted的异步设计中遗漏了一些东西,但我似乎无法找到一种方法来调用sendMessage()方法“externaly”。我的意思是,发送消息而不仅仅是Twisted / AutobahnWebsockets的回调方法(比如onOpen或者在onMessage()上从服务器接收数据)

当然我可以启动一个线程并调用my_protocol_instance.sendMessage(“hello”),但这会破坏异步设计的所有目的吗?

在一个具体的例子中,我需要一个顶级包装类来打开连接并管理它,每当我需要时,我都会调用my_class.send_my_toplevel_message(msg)。我该如何实现呢?

希望我对我的解释一直很清楚。

由于

2 个答案:

答案 0 :(得分:3)

为什么需要一个线程来启动protocolInstance.sendMessage()? 这可以在正常的反应器回路中完成。

扭曲的核心是反应堆,当你考虑扭曲自己反应时,它可以更容易地看待事物 - 这意味着它可以作为对其他东西的反应(响应)。

现在我假设您正在谈论的线程也会因为某些事件或活动或状态而在调用sendMessage时被创建。我很难想象你只需要在没有任何理由做出反应的情况下发出消息的情况。

但是如果有一个事件应该触发sendMessage,则不需要在线程中调用它:只需使用twisted机制来捕获该事件,然后从该特定事件的回调中调用sendMessage。

现在谈谈你的具体例子:你能指出什么“我需要的时候”完全意味着在这个问题的背景下?来自另一个连接的输入?来自用户的输入?循环活动?

答案 1 :(得分:2)

我设法通过在另一个线程中运行Twisted来实现我所需要的,保持我的程序可以自由运行并允许它在Twisted with reactor.callFromThread()中触发发送数据。

您怎么看?

# ----- twisted ----------
class _WebSocketClientProtocol(WebSocketClientProtocol):
    def __init__(self, factory):
        self.factory = factory

    def onOpen(self):
        log.debug("Client connected")
        self.factory.protocol_instance = self
        self.factory.base_client._connected_event.set()

class _WebSocketClientFactory(WebSocketClientFactory):
    def __init__(self, *args, **kwargs):
        WebSocketClientFactory.__init__(self, *args, **kwargs)
        self.protocol_instance = None
        self.base_client = None

    def buildProtocol(self, addr):
        return _WebSocketClientProtocol(self)
# ------ end twisted -------

class BaseWBClient(object):

    def __init__(self, websocket_settings):
        self.settings = websocket_settings
        # instance to be set by the own factory
        self.factory = None
        # this event will be triggered on onOpen()
        self._connected_event = threading.Event()
        # queue to hold not yet dispatched messages
        self._send_queue = Queue.Queue()
        self._reactor_thread = None

    def connect(self):
        log.debug("Connecting to %(host)s:%(port)d" % self.settings)
        self.factory = _WebSocketClientFactory(
                                "ws://%(host)s:%(port)d" % self.settings,
                                debug=True)
        self.factory.base_client = self
        c = connectWS(self.factory)
        self._reactor_thread = threading.Thread(target=reactor.run,
                                               args=(False,))
        self._reactor_thread.daemon = True
        self._reactor_thread.start()

    def send_message(self, body):
        if not self._check_connection():
            return
        log.debug("Queing send")
        self._send_queue.put(body)
        reactor.callFromThread(self._dispatch)

    def _check_connection(self):
        if not self._connected_event.wait(timeout=10):
            log.error("Unable to connect to server")
            self.close()
            return False
        return True

    def _dispatch(self):
        log.debug("Dispatching")
        while True:
            try:
                body = self._send_queue.get(block=False)
            except Queue.Empty:
                break
            self.factory.protocol_instance.sendMessage(body)

    def close(self):
        reactor.callFromThread(reactor.stop)