1
votes

I'm writing a simple TCP based network application in Qt and wanted to use QDataStream and QByteArray to send data over the network. The problem is that when I'm putting data into QByteArray they are "zeroed". For example (a slot in MainWindow that is connected to timer timeout signal):

void MainWindow::SendPlayer1Data(){
  QByteArray block;
  QDataStream s(&block, QIODevice::OpenModeFlag::ReadWrite);
  QString h="hello";
  s<<h;
  qDebug() << "h:        " << data;
  qDebug() << "block:    " << QString(block); // equivalent to s >> h on receiving end
  qDebug() << "block[0]: " << int(block[0]);
}

h:         "hello"
block:     ""
block[0]:  0

I receive "hello" once at the beginning but after that I only get "". The same goes for qint32. Both client and server shows that QByteArray size is 14 bytes, so QDataStream writes data into that array, but it makes them 0 (it shows "" when I use s >> h and then use qDebug() << h)

2

2 Answers

0
votes

The issue here is writing a QString directly to a stream that is expecting a QByteArray, consider the following

QByteArray block;
QDataStream s(&block, QIODevice::OpenModeFlag::ReadWrite);
QString h = "hello";
s << h;
qDebug() << block;

Which outputs

"\x00\x00\x00\n\x00h\x00""e\x00l\x00l\x00o"

So the data is there, it just isn't there how one might expect it. The easiest way to solve this is to create a QByteArray from a string encoded with UTF8 (or other encoding of your choice). This can trivially be done on the fly,

QByteArray block;
QDataStream s(&block, QIODevice::OpenModeFlag::ReadWrite);
QString h = "hello";
QByteArray data(h.toUtf8(), 5);
s << data;
qDebug() << block;

Which outputs

"\x00\x00\x00\x05hello"

Because when this QByteArray is sent through the QDataStream the length of the array and 3 NULL characters are prepended - the NULL characters are there in case the buffer is larger than a relatively small 5 (you can test that for yourself by passing a larger value - a small factor of 256 is most demonstrative - as the second parameter in the QByteArray constructor as this is the buffer length). But if you try to explicitly construct a QString (as s >> h does) from the NULL-commenced QByteArray it will create an empty string. To correct for this you can use QByteArray::remove() to remove the first 4 bytes like this

QByteArray block;
QDataStream s(&block, QIODevice::OpenModeFlag::ReadWrite);
QString h = "hello";
QByteArray data(h.toUtf8());
s << data;
qDebug() << QString::fromUtf8(block.remove(0, 4));

Which outputs

"hello"

Complete example

#include <qbytearray.h>
#include <qdatastream.h>
#include <qdebug.h>

int main() {
  QByteArray block;
  QDataStream s(&block, QIODevice::OpenModeFlag::ReadWrite);
  QString h = "hello";
  QByteArray data(h.toUtf8());
  s << data;
  qDebug() << QString::fromUtf8(block.remove(0, 4));
}
0
votes

Ok, I have figured it out. The problem was not the QByteArray or socket because, as @William Miller mentioned, the data was there. The problem was with QDataStream on client side - I decided to create a new QDataStream object every time the slot responsible for receiving data was called. This way I was able to pack data easily into QByteArray, send it and receive every time. The client function for receiving:

void ClientTcpHelper::ReceivePacket(){

if(socket.waitForReadyRead(20)){

    //qDebug()<<"Packet: "<<socket.peek(30)<<endl;
    qDebug()<<"Receiving packet!"<<endl;

    Data=socket.readAll();
    emit DataReceived();


}
else{
    //qDebug()<<"Failed to receive packet!"<<endl;

}}

and unpacking data to variables:

void ClientTcpHelper::UnpackData(){

stream=new QDataStream (&this->Data,QIODevice::OpenModeFlag::ReadWrite);
*stream>>h>>a>>b;
Data.clear();
delete stream;}

h,a and b are members of a class.

Unfortunately I can not explain why QDataStream need to be destroyed every time here in order to handle data as I wanted it from the beginning.