我有一个很奇怪的问题,使用QDataStream,或者至少对我来说很奇怪,因为我根本不了解这种行为。

当然,我的问题来自一个大项目,但是我设法用一个最小的例子重现了这种奇怪的行为,我将在下面进行描述。

我有两节课

  • 二进制数据读取器Reader
  • 二进制数据解析器Parser
  • Reader通过QTcpSocket读取数据,将每个接收到的数据块打包到QByteArray中,然后通过Qt信号将该数组发送到Parser
    Parser将所有接收到的数据块写入其自己的QDataStream,然后将解析该流中的数据包。

    Parser将数据从接收到的QByteArray写入其QDataStream时,就会出现问题。 QDataStream::writeRawData的返回值正确返回写入的字节数,但是QDataStream::atEnd返回true,而QDataStream::device.bytesAvailable返回零。

    为什么? 数据QDataStream::writeRawData声称写在哪里?

    您可以在这篇文章的和找到代码。

    环境:Windows 7 Enterprise SP1 64位上的 Qt 5.9.1(MSVC 2015,32/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
    

    最佳答案

    问题不在于没有数据,而是您的当前位置在数据的末尾。使用device()检索用于包装QBufferQByteArray并重置该对象的位置。

    mStream.device()->reset()
    

    关于c++ - 调用QDataStream::writeRawData后,QDataStream为空,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55146979/

    10-11 15:59