[英]Qt app using QNetworkAccessManager crashes when built for WebAssembly
我正在嘗試制作簡單的應用程序,通過 REST api 從服務器下載信息。 對於下載數據,我使用QNetworkAccessManager 。 我的應用程序在為桌面構建時運行良好,我正在嘗試為WebAssembly構建它。
我在用:
我已經使用-feature-thread
從源代碼構建了 Qt webassembly 以支持多線程,並且 memory / workers(線程)設置是
QMAKE_WASM_PTHREAD_POOL_SIZE = 8
QMAKE_WASM_TOTAL_MEMORY = 3GB
當我將它用作本機桌面時,一切正常。 當我通過瀏覽器(WebAssembly)啟動它時,應用程序啟動,GUI 出現,我可以輸入連接地址。 當我編寫一個有效的(支持 CORS 的服務器)時,請求沒有問題並返回有效的 JSON 文檔。 但是當我輸入無效地址時,應用程序崩潰。
崩潰是由 Qt 中某處的abort()
function (調用堆棧)引起的。 此外,應用程序的行為不一致 - 有時會在第一次請求時發生崩潰,有時在第三次請求時發生。
我遇到了兩個可能的錯誤 - Uncaught RuntimeError: abort(undefined)
和Uncaught RuntimeError: abort(Runtime error: The application has corrupted its heap memory area (address zero)!)
。
我為QNetworkAccessManager嘗試了多個教程,但結果相同。
主文件
#include "DummyRequestManager.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
DummyRequestManager c;
c.init();
return app.exec();
}
DummyRequestManager.hpp
#ifndef DUMMYREQUESTMANAGER_H
#define DUMMYREQUESTMANAGER_H
#include <QQuickView>
#include <QObject>
#include <QNetworkAccessManager>
#include <QJsonDocument>
#include <memory>
enum HttpRespStatus
{
NoResponse = 0,
Ok = 200,
NoContent = 204,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406, // Shouldn't be used.
Conflict = 409,
Gone = 410,
UnsupportedMediaType = 415,
InternalError = 500,
NotImplemented = 501,
ServiceUnavailable = 503
};
class DummyRequestManager : public QNetworkAccessManager
{
Q_OBJECT
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
public:
DummyRequestManager();
void init();
QString url() const { return url_; }
public slots:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData);
QNetworkReply* get(QNetworkRequest &request);
void sendRequest();
public slots:
// Setters
void setUrl(QString url);
signals:
void urlChanged(QString url);
private:
int port_{0};
QString scheme_{"https"};
QString url_{""};
QString finalUrl_{""};
QQuickView view_;
};
#endif // DUMMYREQUESTMANAGER_H
DummyRequestManager.cpp
#include "DummyRequestManager.h"
#include <QQmlContext>
#include <QNetworkReply>
#include <QJsonObject>
DummyRequestManager::DummyRequestManager()
{
connect(this, &DummyRequestManager::urlChanged, this, [=](){
QStringList split = url_.split(":");
QString addr = "";
if(split.size() == 2)
{
addr = split.first();
port_ = split.last().toInt();
finalUrl_ = QString("https://%1:%2/api").arg(addr).arg(QString::number(port_));
qDebug() << finalUrl_;
}
});
}
void DummyRequestManager::init()
{
port_ = 8080;
// WARNING Code below is supposed to be only for testing, it's necessary to add certificate
#ifndef QT_NO_SSL
QSslConfiguration sslConf = QSslConfiguration::defaultConfiguration();
sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
QSslConfiguration::setDefaultConfiguration(sslConf);
#endif
scheme_ = "https";
view_.rootContext()->setContextProperty("dummy", this);
view_.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view_.show();
}
QNetworkReply *DummyRequestManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
return QNetworkAccessManager::createRequest(op, request, outgoingData);
}
QNetworkReply *DummyRequestManager::get(QNetworkRequest &request)
{
qDebug() << "request url" << request.url();
request.setRawHeader("accept", "application/json");
request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
return QNetworkAccessManager::get(request);
}
void DummyRequestManager::sendRequest()
{
QUrl url(finalUrl_ + QString("/info"));
url.setScheme(scheme_);
QNetworkRequest request(url);
QNetworkReply *reply = get(request);
QObject::connect(reply, &QNetworkReply::finished, [=] {
qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();;
qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
qDebug().noquote() << doc.toJson(QJsonDocument::Indented);
reply->deleteLater();
});
}
void DummyRequestManager::setUrl(QString url)
{
if (url_ == url)
return;
url_ = url;
emit urlChanged(url_);
}
main.qml
import QtQuick 2.12
Item {
width: 640
height: 480
visible: true
Rectangle {
id: centerRect
width: 200
height: 200
anchors.centerIn: parent
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
dummy.sendRequest()
}
}
Text {
anchors.centerIn: parent
text: qsTr("Send")
color: "white"
}
}
Rectangle {
color: "gray"
width: centerRect.width
height: 50
anchors.top: centerRect.top
anchors.horizontalCenter: centerRect.horizontalCenter
TextInput {
anchors.fill: parent
onTextChanged: {
dummy.url = text
}
}
}
}
你知道問題可能出在哪里嗎?
謝謝您的幫助!
編輯:當我刪除 lambda function 中的 reply- reply->deleteLater()
時,它不會崩潰。
一般來說,WebAssembly 對 QNetworkAccessManager 的支持是有限的,因為瀏覽器負責實際的 HTTP 請求,因此不支持 SSL 處理,例如,您不能設置任何客戶端證書。 此外,Qt(至少 5.14)中的多線程支持非常有限且有問題,但單線程構建應該沒問題。 QML 應用程序通常在 WebAssembly 的單線程構建中運行得很好,我知道因為我是 Felgo 努力將生產應用程序引入 wasm 的一部分,你可以在我們的webC845E056C50507A中測試我們的結果(全單線程) felgo.com/web-editor或我們的示例部分http://felgo.com/try-wasm
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.