QDataStream пусто после вызова QDataStream :: writeRawData - PullRequest
2 голосов
/ 13 марта 2019

У меня довольно странная проблема, с использованием QDataStream, или, по крайней мере, для меня это странно, потому что я совсем не понимаю поведение.

Конечно, моя проблема связана с большим проектом, но мне удалось воспроизвести это странное поведение на минимальном примере, который я сейчас опишу.

У меня есть два класса

  1. считыватель двоичных данных Reader и
  2. парсер двоичных данных Parser.

Reader читает данные через QTcpSocket, упаковывает каждый полученный блок данных в QByteArray и отправляет этот массив через сигнал Qt на Parser.

Parser записывает все полученные фрагменты данных в свой собственный QDataStream и впоследствии должен анализировать пакеты из этого потока.

Проблема возникает, когда Parser записывает данные из полученного QByteArray в его QDataStream. Возвращаемое значение QDataStream::writeRawData правильно возвращает количество записанных байтов, но затем QDataStream::atEnd возвращает true, а QDataStream::device.bytesAvailable возвращает ноль.

Почему? Где данные QDataStream::writeRawData утверждают, что написали?

Вы можете найти код в и этого сообщения.

Среда: Qt 5.9.1 (MSVC 2015, 32/64 бит) в Windows 7 Enterprise SP1 64 бит


Reader.h

#ifndef READER_H
#define READER_H

#include <QAbstractSocket>
#include <QByteArray>
#include <QDataStream>
#include <QHostAddress>
#include <QObject>

class Reader : public QObject
{
    Q_OBJECT

public:
    Reader(const QHostAddress ip, quint16 port);
    virtual ~Reader();

signals:
    void signalNewData(const QByteArray data);

private slots:
    void slotOnReadyRead();

private:
    QAbstractSocket *mSocket;
    QDataStream mStream;
};

#endif // READER_H

Reader.cpp

#include "reader.h"

#include <QTcpSocket>

Reader::Reader(const QHostAddress ip, quint16 port)
    : mSocket(new QTcpSocket(this))
    , mStream()
{
    mStream.setDevice(mSocket);
    mStream.setVersion(QDataStream::Qt_5_9);
    mStream.setByteOrder(QDataStream::LittleEndian);

    connect(mSocket, &QIODevice::readyRead, this, &Reader::slotOnReadyRead);

    mSocket->connectToHost(ip, port, QIODevice::ReadOnly);
}

Reader::~Reader()
{
    mSocket->disconnectFromHost();
    delete mSocket;
    mSocket = nullptr;
}

void Reader::slotOnReadyRead()
{
    mStream.startTransaction();

    quint64 availableBytesForReading = mStream.device()->bytesAvailable();
    QByteArray binaryDataBlock;
    char *tmp = new char[availableBytesForReading];
    mStream.readRawData(tmp, availableBytesForReading);
    binaryDataBlock.append(tmp, availableBytesForReading);
    delete[] tmp;
    tmp = nullptr;

    if (mStream.commitTransaction())
    {
        emit signalNewData(binaryDataBlock);
    }
}

parser.h

#ifndef PARSER_H
#define PARSER_H

#include <QByteArray>
#include <QDataStream>
#include <QObject>

class Parser : public QObject
{
    Q_OBJECT

public:
    Parser();

public slots:
    void slotOnNewData(const QByteArray data);

private:
    QDataStream mStream;
};

#endif // PARSER_H

Parser.cpp

#include "parser.h"
#include <QDebug>

Parser::Parser()
    : mStream(new QByteArray(), QIODevice::ReadWrite)
{
    mStream.setVersion(QDataStream::Qt_5_9);
    mStream.setByteOrder(QDataStream::LittleEndian);
}

void Parser::slotOnNewData(const QByteArray data)
{
    const char *tmp = data.constData();
    int numberOfBytesWritten = mStream.writeRawData(tmp, data.length());

    qDebug() << "numberOfBytesWritten:" << numberOfBytesWritten << endl;
    qDebug() << "QDataStream::status():" << mStream.status() << endl;
    qDebug() << "QDataStream::atEnd():" << mStream.atEnd() << endl;
    qDebug() << "QDataStream::device.bytesAvailable():" << mStream.device()->bytesAvailable() << endl;
}

main.cpp

#include <QCoreApplication>
#include "reader.h"
#include "parser.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Reader *reader = new Reader(QHostAddress("<insert IP>"), <insertPort>);
    Parser *parser = new Parser();

    QObject::connect(&a, &QCoreApplication::aboutToQuit, reader, &QObject::deleteLater);
    QObject::connect(&a, &QCoreApplication::aboutToQuit, parser, &QObject::deleteLater);

    QObject::connect(reader, &Reader::signalNewData, parser, &Parser::slotOnNewData);

    return a.exec();
}

delete.pro Да, я назвал мой минимальный пример проекта «delete»: «D

QT += core network
QT -= gui

CONFIG += c++11

TARGET = delete
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp \
    reader.cpp \
    parser.cpp

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

HEADERS += \
    reader.h \
    parser.h

1 Ответ

1 голос
/ 13 марта 2019

Проблема не в том, что нет данных, а в том, что ваша текущая позиция находится в конце данных.Используйте device(), чтобы получить QBuffer, который использовался для упаковки QByteArray, и сбросить положение этого объекта.

mStream.device()->reset()
...