从tornado websocket客户端向websocket服务器发送消息并等待响应

时间:2018-03-25 13:21:44

标签: python-2.7 websocket client tornado


我是龙卷风的初学者,通常是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()并且不会执行该元素,而是等待/收听传入的消息等...
我也对龙卷风中的连接类型感到困惑。没有很多websocket连接的例子,而是有很多关于HTTP的例子。因为所有这些对我来说都是新的,所以也许我误解了一些事情,但我认为只有初始握手是基于http的http来基于websockets我不能使用像RequestHandlertornado.httpclient这样的类

1 个答案:

答案 0 :(得分:0)

我不知道如何以收益的方式做到这一点,但我可以看到一个非常简单的回调方式解决方案,如果你对它好的话。您可以通过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)