简体   繁体   English

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

[英]Writing an “interactive” client with Twisted/Autobahn Websockets

Maybe I'm missing something here in the asynchronous designs of Twisted, but I can't seem to find a way to call the sendMessage() method "externaly". 也许我在Twisted的异步设计中遗漏了一些东西,但我似乎无法找到一种方法来调用sendMessage()方法“externaly”。 By this I mean, sending messages without being solely at the callback methods of Twisted/AutobahnWebsockets (like at onOpen or when receiving data from server at onMessage()) 我的意思是,发送消息而不仅仅是Twisted / AutobahnWebsockets的回调方法(比如onOpen或者在onMessage()上从服务器接收数据)

Of course I could launch a thread and call my_protocol_instance.sendMessage("hello") but that would defeat every purpose of the asynchronous design right? 当然我可以启动一个线程并调用my_protocol_instance.sendMessage(“hello”)但这会破坏异步设计的所有目的吗?

In a concrete example, I need to have a top wrapper class which opens the connection and manages it, and whenever I need I call my_class.send_my_toplevel_message(msg). 在一个具体的例子中,我需要一个顶级包装类来打开连接并管理它,每当我需要时,我都会调用my_class.send_my_toplevel_message(msg)。 How can I implement this? 我该如何实现呢?

Hope I've been clear on my explanation. 希望我对我的解释一直很清楚。

Thanks 谢谢

Why do you need a thread to launch protocolInstance.sendMessage() ? 为什么需要一个线程来启动protocolInstance.sendMessage()? This can be done in a normal reactor loop. 这可以在正常的反应器回路中完成。

The core of a twisted is reactor and it gives a much easier look at things when you consider twisted itself reactive - meaning it does something as a reaction (response) to something else. 扭曲的核心是反应堆,当你考虑扭曲自身的反应时,它可以更容易地看待事物 - 这意味着它可以作为对其他事物的反应(反应)。

Now I assume that the thread you are talking about, also gets created and made in calling sendMessage because of certain events or activity or status. 现在我假设您正在讨论的线程也会因为某些事件或活动或状态而在调用sendMessage时被创建。 I can hardly imagine a case where you would just need to send a message out of the blue without any reason to react. 我很难想象你只需要在没有任何理由做出反应的情况下发出消息的情况。

If however there is an event which should trigger sendMessage, there is no need to invoke that in thread: just use twisted mechanisms for catching that event and then calling sendMessage from that particular event's callback. 但是,如果有一个事件应该触发sendMessage,则不需要在线程中调用它:只需使用扭曲机制来捕获该事件,然后从该特定事件的回调中调用sendMessage。

Now on to your concrete example: can you specify what "whenever I need" means exactly in the context of this question? 现在谈谈你的具体例子:你能指出什么“我需要的时候”完全意味着在这个问题的背景下? An input from another connection? 来自另一个连接的输入? An input from the user? 来自用户的输入? Looping activity? 循环活动?

I managed to implement what I needed by running Twisted in another thread, keeping my program free to run and allowing it to trigger send data in Twisted with reactor.callFromThread(). 我设法通过在另一个线程中运行Twisted来实现我所需要的,保持我的程序可以自由运行并允许它在Twisted with reactor.callFromThread()中触发发送数据。

What do you think? 你怎么看?

# ----- 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM