QNetworkReply返回不完整的XML数据

时间:2017-11-08 11:01:00

标签: c++ xml qt http-get qnetworkreply

我向远程服务器发送HTTP GET请求。使用各种参数,我定义了我感兴趣的内容。特别是我确保output=xml在查询中,因为它使服务器以XML格式返回答复。

我的班级HttpRetriever与相应的QNetworkReplyQNetworkAccessManager之间有以下联系(QNetworkRequest请参阅 slotStartRequest()) :

connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
        SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);

此处感兴趣的插槽具有以下声明:

slotFinishRequest():

void HttpRetriever::slotFinishRequest()
{
    LOG(INFO) << "Finishing HTTP GET request from URL \"" << this->url.toString() << "\"";
    this->reply = Q_NULLPTR;

    // Reset validity of reply from a previous request
    this->validReply = false;
    // Skip validation if it's disabled
    if (!this->validateReply)
    {
        LOG(WARNING) << "Validation disabled. In order to enable it see the \"validate\" and \"validationMode\" in \"settings.ini\"";
        this->validReply = true;
    }
    else
    {
        // Validate reply
        this->validReply = validateReply();
    }

    if (!this->validReply)
    {
        return;
    }

    processReply(); // Parsing

    this->processingRequest = false;
}

slotReadyReadRequest():

void HttpRetriever::slotReadyReadRequest()
{
    LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
    this->bufferReply = this->reply->readAll();
}

slotFinishRequest()内,我致电processReply()

void HttpRetriever::processReply()
{
    LOG(INFO) << "Processing reply for request \"" << this->url.toString() << "\"";
    LOG(DEBUG) << QString(this->bufferReply);
    // Process the XML from the reply and extract necessary data

    QXmlStreamReader reader;
    reader.addData(this->bufferReply);

    // Read the XML reply and extract required data
    // TODO
    while (!reader.atEnd())
    {
        LOG(DEBUG) << "Reading XML element";
        reader.readNextStartElement();

        QXmlStreamAttributes attributes = reader.attributes();
        foreach (QXmlStreamAttribute attrib, attributes)
        {
            LOG(DEBUG) << attrib.name();
        }
    }
    if (reader.hasError())
    {
        LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
    }

    LOG(INFO) << "Sending data to data fusion handler";
    // TODO
}

我通过以下插槽触发HTTP get请求:

void HttpRetriever::slotStartRequest(quint32 id)
{
    if (this->processingRequest)
    {
        this->reply->abort();
    }

    this->processingRequest = false;

    // The first request also instantiates the manager. If the slot is called after the instance of HafasHttpRetriever
    // is moved to a new thread this will ensure proper parenting
    if (!this->manager)
    {
        this->manager = new QNetworkAccessManager(this);
    }

    quint32 requestId = generateRequestId(stopId);
    if (!this->url.hasQuery())
    {
        LOG(WARNING) << "Attempting to start request without parameters";
    }

    // Part of the filters applied to the request to reduce the data received (for more see slotSetRequestParams())
    QUrlQuery query(this->url.query());
    query.addQueryItem("input", QString::number(requestId));
    // TODO Add more filters; see documentation

    this->url.setQuery(query);

    LOG(INFO) << "Requesting data from \"" << this->url.toString() << "\" with request ID:" << requestId;

    QNetworkRequest request(this->url);
    this->reply = this->manager->get(request);

    // Establish connections from/to the reply and the network access manager
    connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
            SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
    connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
    connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
    connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
}

正如您所看到的那样,我已经为我的类和服务器之间的网络通信奠定了基础,我还没有开始解析XML回复并从中提取我需要的信息。

问题是我(非常,经常)得到

  

解析XML数据时遇到错误:期望开始标记。

  

解析XML数据时遇到错误:文档过早结束

在我的processReply()函数中。每当我得到一个大的回复(几百到几万行)时就会发生这种情况。当我得到一个小的(30-40行给予或接受)时,它永远不会发生。

所以问题显然在于我收到的数据量,它由QNetworkAccessManager(或者所有这些中的Qt组件缓冲接收到的数据块)和/或者我在班级中设置网络相关组件实例的方式。我还必须在这里做一个重要的注意事项,即在我的浏览器中(带有 HttpRequester 附加组件的最新Firefox)我总是收到完整的XML,无论它有多大。所以这似乎是我的应用程序独有的问题,与我系统上的网络设置无关。

1 个答案:

答案 0 :(得分:0)

因为@Marco没有写出答案......

问题是我通过分配QNetworkReply::readAll()的结果一直在重写我的缓冲区。正如建议使用QByteArray::append()解决问题。

为了防止此解决方案可能出现问题,即您继续附加每次下一个回复,{} {}需要在某个时刻调用,例如当QByteArray::clear()信号为发射。当然,需要首先处理其内容,然后将其冲洗干净。