【问题标题】:can I access QtWebEngine's underlying IPC without QWebChannel?我可以在没有 QWebChannel 的情况下访问 QtWebEngine 的底层 IPC 吗?
【发布时间】:2017-02-04 04:53:41
【问题描述】:

QtWebEngine 使用 IPC 机制在 C+ Qt 世界和 JavaScript 工作之间进行通信。这种机制用于 QWebChannel,它似乎是基于 WebSockets 的。有没有办法在不使用 QWebChannel 的情况下使用底层 IPC 或 WebSockets,因为后者似乎仅限于字符串或 JSON 编码的数据?

背景:我编写了一个应用程序QtDomTerm,它是一个基于 JavaScript 的终端仿真器,它使用 QWebChannel 将 PTY 的输入/输出连接到 QtWebEngine。这工作得相当好,但有一个与 utf8/字符串转换有关的小故障。理想情况下,我想从 PTY 发送原始字节,并在 JavaScript 中进行字节到文本的转换。但是 QWebChannel 太高级了,只处理字符串或 JSON 编码的数据。它不处理 QByteArray。

当然有多种方法可以解决我的问题。一种是手动创建一个 WebSocket 服务器,并让运行在 QtWebEngine 中的 JavaScript 连接到它。但无论如何,这似乎就是幕后发生的事情,使用qt.webChannelTransport。如果我可以访问底层传输(WebChannelIPCTransportHost 类似乎是相关的),这似乎是最有效和最优雅的。

有人试过这样的吗? IE。我想使用 QWebChannel - 除非有一种有效的方法可以传递 QByteArray。

(我重新表述了这个问题。有一条关于缺少研究的评论,但我已经大量浏览了 Qt 文档、源代码和此处,但没有找到明确的答案。)

【问题讨论】:

  • 它在哪里说 QWebChannel 仅限于 JSON 编码的数据字符串。我最近遇到了一个问题——我的自定义结构——即使放入 QVariant 也无法通过 QWebChannel 发送到 JavaScript 脚本。如果我能找到这方面的 QT 参考,那就太好了——它没有记录在 QWebChannel 中。
  • doc.qt.io/qt-5.11/qtwebchannel-javascript.html 说 send() “接受字符串化的 JSON 消息”。 doc.qt.io/qt-5.11/qwebchannelabstracttransport.html 类发送和接收 QJsonObjects。

标签: qt websocket qbytearray qtwebengine


【解决方案1】:

是什么阻止您发送QString::fromLatin1(data.toHex()),其中data 属于QByteArray 类型?这就是你所需要的,真的。在 javascript 端使用反向转换,参见例如this question.

【讨论】:

  • 是的,将字节转换为十六进制并传输结果字符串当然是可行的(并且相对简单)。但不可否认,它效率低下,因为它使传输的数据量增加了一倍。它也冒犯了我的优雅感:为什么将字节转换为十六进制 - 当我们使用字节协议时。在我的应用程序中,我真的不需要 QWebChanell 的功能 - 我只需要一个简单的双向字节通道,因为我已经有了一个用于“带外”消息的机制。
  • 你不能使用常规的 TCP 套接字和 websockets 吗?
  • 使用“TCP socket and websockets”很好(本质上是我想做的) - 但不是创建一个新的监听器和连接,我认为它会更简单,更健壮在现有的 IPC 连接上。 (例如,并非所有环境都可以访问 TCP 套接字。) QWebChannel 已经建立了连接;我只想在低(字节流)级别使用现有连接,而不是更高的 QWebChannel 级别。 (我更新了问题以使其更清楚。)
  • 你可以使用Qt的私有实现细节来做。如果您询问公共 API 是否支持它:它不支持。由于您正在编译自己的 Qt 副本,因此这应该不是问题。您甚至可以修补您的副本以添加较低级别的功能。您将 Qt 保留在您的源代码管理中,因为它是您产品的一个组成部分。正确的?正确的? :)
  • 我目前使用的是安装在 Fedora 上的未经修改的 Qt。即使我正在编译它,进行和维护私有修改也会比替代解决方案更丑陋和不可取,即使它们比我想要的更丑陋。在我的源代码管理中维护私有 Qt 不适合 DomTerm 项目。
【解决方案2】:

由于似乎没有办法“低于” QWebChannel(无需破解 Qt 本身),因此似乎有三种选择:

  • 创建一个单独的 WebSockets 连接。这将需要更大的非本地更改,并可能创建额外的流程。

  • 在 C++ 端进行字节到文本解码,可能使用 QTextDecoder。那可能是最简单的。然而,有架构上的原因更喜欢在我们进行转义序列处理的地方进行文本解码。这当然更传统,并且可能更好地处理极端情况。另外,DomTerm 需要与 QtDomTerm 前端以外的其他前端一起工作。

  • 将字节流编码为字符串序列,使用 QWebChannel 传输后者,在 JavaScript 端转换回字节,然后在 JavaScript 中处理文本解码。

我选择实施第三个选项。后端转换有一些开销。但是,我设计并实现了一种非常有效的编码:

/** Encode an arbitrary sequence of bytes as an ASCII string.
 * This is used because QWebChannel doesn't have a way to transmit
 * data except as strings or JSON-encoded strings.
 * We restrict the encoding to ASCII (i.e. codes less then 128)
 * to avoid excess bytes if the result is UTF-8-encoded.
 *
 * The encoding optimizes UTF-8 data, with the following byte values:
 * 0-3: 1st byte of a 2-byte sequence encoding an arbitrary 8-bit byte.
 * 4-7: 1st byte of a 2-byte sequence encoding a 2-byte UTF8 Latin-1 character.
 * 8-13: mean the same ASCII control character
 * 14: special case for ESC
 * 15: followed by 2 more bytes  encodes a 2-byte UTF8 sequence.
 * bytes 16-31: 1st byte of a 3-byte sequence encoding a 3-byte UTF8 sequence.
 * 32-127: mean the same ASCII printable character
 * The only times we generate extra bytes for a valid UTF8 sequence
 * if for code-points 0-7, 14-26, 28-31, 0x100-0x7ff.
 * A byte that is not part of a valid UTF9 sequence may need 2 bytes.
 * (A character whose encoding is partial, may also need extra bytes.)
 */
static QString encodeAsAscii(const char * buf, int len)
{
    QString str;
    const unsigned char *ptr = (const unsigned char *) buf;
    const unsigned char *end = ptr + len;
    while (ptr < end) {
        unsigned char ch = *ptr++;
        if (ch >= 32 || (ch >= 8 && ch <= 13)) {
            // Characters in the printable ascii range plus "standard C"
            // control characters are encoded as-is
            str.append(QChar(ch));
        } else if (ch == 27) {
            // Special case for ESC, encoded as '\016'
            str.append(QChar(14));
        } else if ((ch & 0xD0) == 0xC0 && end - ptr >= 1
                 && (ptr[0] & 0xC0) == 0x80) {
            // Optimization of 2-byte UTF-8 sequence
            if ((ch & 0x1C) == 0) {
                // If Latin-1 encode 110000aa,10bbbbbb as 1aa,0BBBBBBB
                // where BBBBBBB=48+bbbbbb
              str.append(4 + QChar(ch & 3));
            } else {
                // Else encode 110aaaaa,10bbbbbb as '\017',00AAAAA,0BBBBBBB
                // where AAAAAA=48+aaaaa;BBBBBBB=48+bbbbbb
                str.append(QChar(15));
                str.append(QChar(48 + (ch & 0x3F)));
            }
            str.append(QChar(48 + (*ptr++ & 0x3F)));
        } else if ((ch & 0xF0) == 0xE0 && end - ptr >= 2
                 && (ptr[0] & 0xC0) == 0x80 && (ptr[1] & 0xC0) == 0x80) {
            // Optimization of 3-byte UTF-8 sequence
            // encode 1110aaaa,10bbbbbb,10cccccc as AAAA,0BBBBBBB,0CCCCCCC
            // where AAAA=16+aaaa;BBBBBBB=48+bbbbbb;CCCCCCC=48+cccccc
            str.append(QChar(16 + (ch & 0xF)));
            str.append(QChar(48 + (*ptr++ & 0x3F)));
            str.append(QChar(48 + (*ptr++ & 0x3F)));
        } else {
            // The fall-back case - use 2 bytes for 1:
            // encode aabbbbbb as 000000aa,0BBBBBBB, where BBBBBBB=48+bbbbbb
            str.append(QChar((ch >> 6) & 3));
            str.append(QChar(48 + (ch & 0x3F)));
        }
    }
    return str;
}

是的,这是矫枉过正,几乎可以肯定是过早的优化,但我忍不住 :-)

【讨论】:

    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 2014-03-23
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    相关资源
    最近更新 更多