【问题标题】:send message to websocket server from tornado websocket client and wait for response从 tornado websocket 客户端向 websocket 服务器发送消息并等待响应
【发布时间】:2023-03-07 11:22:01
【问题描述】:


我是龙卷风的初学者,通常是 websockets/networking。
我想要做的是在桌面窗口应用程序中实现一个 websocket 客户端,即:

  • 从服务器接收一些随机数据,读取它,检查数据并 在外部向某些小部件发出信号
  • 向服务器发送数据(服务器无响应)

(到目前为止,我设法做到了这一点)

  • 在某些外部功能(如在小部件中)向服务器发送数据:
def request_masks_export(self, export_dir):
        wrapped_command = {
            "clientAction": "exportMasks",
            "clientData": {
                "client": "Remote Browser is requesting to export masks files.",
                "exportDir": export_dir
            }
        }
        response = self.current_connection_manager.send_sync(wrapped_command)
        # response from send_sync here

...并等待它得到响应,然后在函数内继续。 我无法理解它来做这件事......

这是我的代码:

class ConnectionManager(QThread):
    on_data = Signal(dict)
    connection_interrupted = Signal()

    _default_url = "ws://localhost:12345/"
    _identifier = {
        "clientAction": "newClientConnected",
        "clientData": {
            "client": "connected from python app: {}".format(os.path.basename(__file__))
        }
    }

    def __init__(self, url=None, timeout=None, parent=None):
        QThread.__init__(self, parent)
        self.ioloop = IOLoop.current()

        if url is not None:
            self.url = url
        else:
            self.url = self._default_url

        if timeout is not None:
            self.timeout = timeout
        else:
            self.timeout = 1

        self.ws_connection = None

    @gen.coroutine
    def connect_to_server(self):
        try:
            start_time = time.time()
            self.ws_connection = yield websocket_connect(self.url)
            self.ws_connection.connect_future.add_done_callback(self.connect_callback)
            print 'Elapsed time of connection: %.1f msec' % (time.time() - start_time) * 1000
            self.send(self._identifier)
        except socket.error as e:
            print e
            if e.errno == errno.ECONNREFUSED:
                print 'Connection has been refused. Awaiting connection...'
                yield gen.sleep(self.timeout)
                self.connect_to_server()
        except Exception as e:
            print "unable to connect websocket server"
            print e

    def run(self):

        self.ioloop.spawn_callback(self.connect_to_server)

        self.ioloop.start()

    def connect_callback(self, future):
        if future.exception() is None:
            self.ws_connection = future.result()
            self._on_connection_success()
            self.read_message()
        else:
            self.on_connection_error(future.exception())

    @gen.coroutine
    def read_message(self):
        # reading here all messages, except those that are result of def send_sync
        while True:
            msg = yield self.ws_connection.read_message()
            if msg is None:
                self.on_connection_close()
                break
            self.check_data(msg)

    @gen.coroutine
    def send(self, data):
        if isinstance(data, dict):
            json_object = json.dumps(data)
            res = yield self.ws_connection.write_message(json_object)
    #         from here I just send messages that don't need response

    @gen.coroutine
    def send_sync(self, data):
        if isinstance(data, dict):
            json_object = json.dumps(data)
            response = yield self.ws_connection.write_message(json_object)
    #         I want to get response messege from server here to be able to return it 'somehow'

    def check_data(self, raw_msg):
        """
        Callback function when message is received from server.
        Emits signal
        :param raw_msg: unicode message received from server
        :return:
        """
        try:
            if raw_msg is None:
                raise WebSocketNoneError("raw_msg received is None!", "none data")

            try:
                raw_data_dict = json.loads(raw_msg)
            except ValueError as e:
                # handles json decode error from raw_msg(string)
                print e
                raise WebSocketMessageError("Incompatible message type!", "JSON only")

            if raw_data_dict.has_key("serverAction"):
                self.on_data.emit(raw_data_dict)
                return raw_data_dict
            else:
                raise WebSocketMessageError("Incompatible message structure!", "missing serverAction")

        except WebSocketMessageError as e:
            print e.args
            raise
        except WebSocketNoneError as e:
            print e.args
            raise

    def close_manager(self):
        """
        Method for stopping websocket, waiting for thread to finish and stop it.
        :return:
        """
        self.close_websocket()
        self.ioloop.stop()
        self.stop()

    def is_connected(self):
        if not self.ws_connection:
            return False
        else:
            return True

    def stop(self):
        """
        Stop thread.
        :return:
        """
        self.quit()
        self.wait()

    def close_websocket(self):
        """
        Close websocket.
        :return:
        """
        try:
            self.ws_connection.close()
        except Exception as e:
            print e
            pass

    def on_connection_success(self):
        print "_on_connection_success"
        pass

    def on_connection_close(self):
        print "_on_connection_close"
        self.connection_interrupted.emit()
        self.connect_to_server()
        pass

我正在使用:

class ConnectionManager(QThread):

因为它阻塞了用于应用程序其余部分的线程...这是正确的方法吗?如果我错了,请纠正我,但我不会在 QThread 中这样做,然后整个应用程序将加载到 iolopp.start() 点并且不会执行超过该元素,而是等待/监听传入的消息, ETC...
我也对龙卷风中的连接类型有点困惑。 websocket 连接的例子并不多,相反有很多关于 HTTP 的例子。由于所有这些东西对我来说都是新的,所以也许我误解了一些东西,但我认为只有初始握手是基于 websockets 的 http,我不能使用像 RequestHandlertornado.httpclient 这样的类。

【问题讨论】:

  • 我想 PyQT/QT 已经有某种事件循环甚至 websocket 库,你应该使用它们,而不是使用 Tornado 的事件循环和网络工具(你应该只留给你的后端) . Very shallow and fast search shows, that this is quite true.
  • 是的,我明白这一点,但我想使用龙卷风而不是别的。我知道这是可以实现的,对于有经验的龙卷风用户来说可能相当容易。

标签: python-2.7 websocket client tornado


【解决方案1】:

我不知道如何以 yield 方式进行,但我可以看到一个非常简单的回调方式解决方案,如果你同意的话。您可以通过 Tornado 的事件循环和 QT 事件循环之间的交互来实现这一点。我不知道 PyQT,所以我将展示一些抽象和一般概念,而不是工作代码。

def request_masks_export(self, export_dir):
    # ...
    IOLoop.current().add_callback(send_sync, 
                                  wrapped_command,
                                  some_func_to_call_after_request_is_finished)

...

@gen.coroutine
def send_sync(self, data, callback_func):
    if isinstance(data, dict):
        json_object = json.dumps(data)
        response = yield self.ws_connection.write_message(json_object)
        QTEventLoop.add_callback(callback_func, response)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-25
    • 2022-07-15
    • 2017-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多