I have a rather odd problem, using QDataStream
, or at least it is odd to me, because I don't understand the behavior at all.
Of course my problem is from a big project, but I managed to reproduce that odd behavior with a minimal example, which I'll describe now.
I have two classes
Reader
andParser
.The Reader
reads data via a QTcpSocket
, packs each received data chunk into a QByteArray
and sends that array via Qt signal to Parser
.
The Parser
writes all received data chunks to its own QDataStream
and shall parse packets from that stream afterwards.
The problem arises, when Parser
writes the data from the received QByteArray
to its QDataStream
. The return value of QDataStream::writeRawData
correctly returns the number of bytes written, but then QDataStream::atEnd
returns true
and QDataStream::device.bytesAvailable
returns zero.
Why? Where is the data QDataStream::writeRawData
claims to have written?
You can find the code at the and of this post.
Environment: Qt 5.9.1 (MSVC 2015, 32/64 bit) on Windows 7 Enterprise SP1 64 bit
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 Yes, I called my minimal example project '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
The problem is not that there is no data but that your current position is at the end of the data. Use device()
to retrieve the QBuffer
which it used to wrap your QByteArray
and reset the position of that object.
mStream.device()->reset()