PDA

View Full Version : QByteArray problem



Misenko
1st October 2008, 23:13
Hi again.

I have this problem. I have two network applications. First application write into socket QByteArray



QByteArray array = new QByteArray();
QDataStream stream(array, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_4_4);

streamOut << quint32(Data::PluginData) << pluginName << quint32(ProgramViewerMenu::NewImage) << shot.toImage();

first number is 2 and second is 1.

socket->write(array);


In other application I read the first number



QDataStream streamOut(socket);
streamOut.setVersion(QDataStream::Qt_4_4);

quint32 dataType;
streamOut >> dataType;
qDebug() << "data thread dataType" << dataType;

QByteArray array;
array = socket->readAll();


and than te array with remaining data give to other class of this application by signal and slot. Than I read the pluginName which is QString and it read it correctly. Than I pass the array to another class like a argument in function



QDataStream streamOut(array);
streamOut.setVersion(QDataStream::Qt_4_4);

QString name;
streamOut >> name;

menu->readData(array);


In that class I want to read the second number



QDataStream stream(array);
stream.setVersion(QDataStream::Qt_4_4);

quint32 data;
stream >> data;


But this number is not 1 like I wrote but 28 and I dont know why. When I read all data in one class without passing it, it reads it corretly.
So is there any other way how to write to and read from QByteArray? Or do I have to do something to prepare data for passing to another class?
Thank you.

caduel
2nd October 2008, 08:35
Note that readAll reads the available data. You have to make sure that all the data you sent (at least for this package) has arrived. Usually this is done by sending the package size first. Examples somewhere in the Qt docs.

Also note that each time you construct a QDataStream on your QByteArray, the "file pointer" points to the beginning of the QByteArray. The second stream does not know or care what has been read in the first stream.

Solutions:
* you can also read (with QDataStream) directly from the socket. No need to read into a QByteArray. Pass the socket and read from it directly.
* pass a common QDataStream

HTH

yuriry
2nd October 2008, 08:40
I think when you pass an array to menu->readData(array), the first data item in the array is your string. My guess is you need to pass streamOut instead of the array: the stream will be positioned on a correct number that follows the string that you've just read.

Misenko
2nd October 2008, 19:22
Thanks guys.
But I cant pass QDataStream because this error:
initializing argument 1 of `void DataThread::pluginData(QDataStream)'
d:/Programy/QT_4.4.1/include/QtCore/../../src/corelib/io/qdatastream.h `QDataStream::QDataStream(const QDataStream&)' is private

And about passing socket. Application writes quite quickly to this socket and I dont know whether it is a good idea.

Is there any other way how to use QByteArray and tell stream where to read from?

yuriry
2nd October 2008, 19:39
It looks like you are passing QDataStream by value. Try passing by pointer or by reference.

Misenko
2nd October 2008, 22:07
Yes you were right. When I pass QDataStream like pointer error disappeared. But it didnt solve the problem. It didnt read 1 and not even 28 but 0.
Any idea whats wrong?

caduel
2nd October 2008, 22:15
let's trade: code for help?

Misenko
2nd October 2008, 23:19
Of course sorry:

I didnt change writing to the slot only reading from it.



QByteArray array;
array = socket->readAll();
QDataStream *streamOut = new QDataStream(array);
streamOut->setVersion(QDataStream::Qt_4_4);

quint32 dataType;
*streamOut >> dataType;
qDebug() << "data thread dataType" << dataType;

if (dataType == Data::PluginData)
emit pluginDataIn(streamOut);
...
connect(connection, SIGNAL(pluginDataIn(QDataStream*)), plugins, SLOT(dataIn(QDataStream*)));
...




void Plugins::dataIn(QDataStream* streamOut)
{
QString name;
*streamOut >> name;

qDebug() << name;

quint32 type;
*streamOut >> type;

qDebug() << type;

Menu *menu = menus.value(name);
if(!menu)
{
qDebug() << "return menu";
return;
}
menu->readData(streamOut);




void ProgramViewerMenu::readData(QDataStream* stream)
{
if (!isListening)
return;

window->getData(stream);
showWindow();
}

void ViewerWindow::getData(QDataStream* streamOut)
{
quint32 data;
*streamOut >> data;

qDebug() << "viewer window data" << quint32(data);

switch (data)
{
case ViewerWindow::NewImage :
setImage(streamOut);
break;
case ViewerWindow::Pixels :
setPixels(streamOut);
break;
case ViewerWindow::Stop :
clearImage();
emit closing();
break;
}


I hope it helps.
But I will be pleased if it could be do in other way than passing QDataStream*. Maybe somehow use QByteArray or another class I dont know.

caduel
2nd October 2008, 23:37
a) Did you check that your readAll() really did read all the data you sent? (It reads the available data. It is not guaranteed that all you sent is available at once and/or in a single read!). You do need to fix that!
Calling readAll the way you do is not correct: see the fortune client example in the Qt docs:network-fortuneclient-client-cpp The function readClient() 'waits' with processing a message til the complete block has been read. The server sends -as said earlier- the size of the block as the first quint32 of each block.
(Make sure by printing the size of the amount of bytes read.)

b) why don't you wrap the socket in a QDataStream directly, rather than reading into a QByteArray and wrapping that?

Other than esp. a) your code looks basically correct (it is not complete, so obviously there might be hidden issues).
At what point does your code fail (which >> does not read the expected value)?

HTH

yuriry
3rd October 2008, 00:18
I also noticed that you write quint32, QString, quint32 and then shot.toImage().



streamOut << quint32(Data::PluginData) << pluginName << quint32(ProgramViewerMenu::NewImage) << shot.toImage();


But when you read, you read quint32, QString, quint32 and quint32.

Does shot.toImage() returns quint32?

Misenko
3rd October 2008, 17:56
to yuriry: sorry that was my mistake. It should be:



void Plugins::dataIn(QDataStream* streamOut)
{
QString name;
*streamOut >> name;

qDebug() << name;

/* quint32 type;
*streamOut >> type;

qDebug() << type;*/

Menu *menu = menus.value(name);
if(!menu)
{
qDebug() << "return menu";
return;
}
menu->readData(streamOut);
}


It should read quint32, QString, quint32 and QImage. shot is a QPixmap and returning QImage.
I tried to read second quint32 in Plugins::dataIn and it works, quint32 was read correctly, but when I comment this part of code like here and call menu->readData(streamOut); it was read incorrectly in ViewerWindow::getData and I do not know why becouse I only pass the QDataStream* or QByteArray which I tried to pass before.

to caduel: I know I should write size of block first and I will repaire it.
But I know that all data which I write is read because when I read it in one class it read all data corretly but when I pass QDataStream* or QByteArray to another class, in this case from class Plugins to class ProgramViewerMenu and than ViewerWindow, it doesnt read it correctly.

I was wondering if isnt problem that classes ProgramViewerMenu and ViewerWindow are in plugin, in dll file which is loaded when program starts.

And it doesnt work even if I wrap the socket in QDataStream directly i ve just tried it.

yuriry
3rd October 2008, 18:37
Could the problem occur because QByteArray is on the stack and QDataStream is on the heap:



QByteArray array;
array = socket->readAll();
QDataStream *streamOut = new QDataStream(array);


I was wondering that may be when you emit a signal the slot is not called right away but somehow delayed and executed through event mechanism. Then ByteArray goes out of scope and the data that QDataStream refers to become invalid. This is a just a guess, but I would also try this code to double check:



QByteArray* array = new QByteArray();
*array = socket->readAll();
QDataStream *streamOut = new QDataStream(array);

Misenko
3rd October 2008, 20:31
No it didnt work. Any other Ideas?

yuriry
3rd October 2008, 21:59
Yeah, I read up on signals - my suggestion was useless because you use a direct connection and all signals emitted on such connections are executed without any delay (http://doc.trolltech.com/4.3/signalsandslots.html#signals). In addition, QByteArray is implicitly shared (http://doc.trolltech.com/4.3/shared.html#implicit-sharing)...

I do not see anything obvious to suggest. May be only trying this code:



QDataStream *streamOut = new QDataStream(socket->readAll());


But it'll probably change nothing.

Misenko
3rd October 2008, 22:34
Well I ve tried to read directly from socket and pass QTcpSocket* and it works. But it doesnt work correctly.
I think its becouse I write to socket very quickly and also I havent added that data size control yet. But still I do not think that it is a good idea to pass socket.
If think there could be problem if i write some data before all data which was written first are read. Am I wrong?
And can me somebody explain why it works with QTcpSocket and doesnt with QByteArray?

yuriry
3rd October 2008, 23:02
As Caduel pointed out readAll() might not had read all the data that you sent, only the data currently available on the socket.



QByteArray QIODevice::readAll ()

Reads all available data from the device, and returns it as a QByteArray.

This function has no way of reporting errors; returning an empty QByteArray() can mean either that no data was currently available for reading, or that an error occurred.


Try putting a sleep before reading the data from the socket and check readBufferSize() (if it is not 0 then call setReadBufferSize(0) to make it unlimited).

Misenko
4th October 2008, 09:02
So I cant use QByteArray in any way? And why? I still do not understant why it works with QTcpSocket and doesnt with QByteArray

And can me somebody show some piece of code how to correctly write to socket and read from socket? In my case I think I write to socket faster than it could be read from it. Is it possible? And how to do it right?

Thanks to all.

yuriry
4th October 2008, 22:53
Take a look at void Server::sendFortune() (http://doc.trolltech.com/4.3/network-fortuneserver-server-cpp.html) as a write example and void Client::readFortune() (http://doc.trolltech.com/4.3/network-fortuneclient-client-cpp.html) as a read example.