PDA

View Full Version : QByteArray revisited



pdoria
14th January 2008, 19:18
Hi,

I'm having problems extracting contents from a QByteArray...

Given the code:


QByteArray unitData;
unitData=tcpSocket.readAll(); // incoming binary data
.
.
.
const char *c_str = unitData.toHex().data();
cout << "Received: " << c_str << endl;

outputs correctly:

000F353334...yacka yacka...

but when one tries:

void myUnit::checkImei (QByteArray &data)
{
bool ok;
// check the # of bytes in the header
unsigned int nbrBytes = data.left(2).toUInt(&ok,16);
if (!ok) cout << "Conversion impossible!" << endl;
cout << "# bytes: " << nbrBytes << endl;

}

number of bytes is *always* zero! :confused:

Any pointers would be highly appreciated...
Kind regards,
Pedro Doria Meunier

jacek
14th January 2008, 19:24
const char *c_str = unitData.toHex().data();
cout << "Received: " << c_str << endl;

outputs correctly:
000F353334...yacka yacka...
It's a pure accident.

Use: cout << "Received: " << unitData.toHex().data() << endl;
or:
QByteArray hex = unitData.toHex();
cout << "Received: " << hex.data() << endl;

Have you tried printing contents of data.left(2).toHex() in checkImei()?

pdoria
14th January 2008, 19:39
Thanks for replying.

Yes I did. As expected it returned '000F' (the header for the the number of bytes following...)

What I really want is to convert those two bytes into integer...

Kind regards,
Pedro Doria Meunier

jacek
14th January 2008, 19:54
It seems that QByteArray expects digits, not binary data:
#include <QCoreApplication>
#include <QByteArray>

#include <QtDebug>

int main( int argc, char ** argv )
{
QCoreApplication app( argc, argv );

QByteArray b( QByteArray::fromHex("000F") );

bool ok1, ok2;
qDebug() << b.toHex();
qDebug() << b.toUInt( &ok1, 16 );
qDebug() << ok1;
qDebug() << b.toHex().toUInt( &ok2, 16 );
qDebug() << ok2;

return 0;
}

Output:
$ ./ba
"000f"
0
false
15
true

pdoria
15th January 2008, 18:47
I'm still struggling to get anything useful from a QByteArray that has received binary data... :o

The most frustrating thing is, per debugging, seeing that the data is there and one can't ever do something of myBA.left(2).toUInt() ... :mad:

The toHex() function extracts it correctly, though...
something like: 000F313233343536373839303132333435
A 000F header, followed by the actual data...

Size also reports correctly: 17 bytes.

QString objects don't work for me either since their data stops at a \0, of which the expected incoming data is abundant of.... :( (actually most of the headers begin with 00-something...)

I *really* could use the help of you gurus out there on this one, since this is actually diverting me from the true implementation of the software...

Thanks in advance,
Pedro Doria Meunier.

jacek
15th January 2008, 19:48
Have you tried QDataStream?

pdoria
16th January 2008, 00:05
Jacek,

Thanks for the suggestion.

But I wonder...
Why the need to use this when QTcpSocket::readAll does the job?
Furthermore... why can't one extract binary data (using one the **appealing* methods) directly from a QByteArray? Doesn't it feel like BS?

readAll is a very convenient method and it *works*. the problem resides in QByteArray (at least from my newbie point of view ;) )

So, as far as I'm concerned this is still an open question that, for the life of me, I can't seem to overcome...

Kind regards,
Pedro Doria Meunier.

pdoria
16th January 2008, 14:40
bump:

Still stuck. Could someone please provide a good example on how to overcome this?

Thanks in advance,
Pedro Doria Meunier.

jacek
16th January 2008, 15:13
Why the need to use this when QTcpSocket::readAll does the job?
Because readAll() does a different part of the job. QDataStream knows how to extract an uint from a byte array.

QDataStream ds( ... );
ds.setByteOrder( ... );
quint16 i;
ds >> i;

pdoria
17th January 2008, 05:31
Ok.
Thanks for the reply. QDataStream put out a lot of segfaults outputing to a qint16 data[nbrBytes] ... and I'm really not sure this is the way to go (see below)

Let's start over to see if I can make any sense of this...

I have a device that sends binary data -- 16bit values -- it sends this data all at once.

I'm giving out my code so that someone can help me here...

myunit.h



#ifndef CONNTHREAD_H
#define CONNTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <server.h>

class myUnit : public QThread
{
Q_OBJECT

public:
QTcpSocket tcpSocket;
myUnit ( int socketDescriptor, Server *parent );
~myUnit ();
void run ();
void checkImei (QByteArray &bData);

char * peerAddress; // Unit's IP
char imei[15]; // unique unit identifier (IMEI)
bool gotImei; // semaphore for determining whether we have the unit's IMEI or not

//SNIP sensitive objects

signals:
void error ( QTcpSocket::SocketError socketError );

public slots:
void disconnected ();
void incomingData ();
quint64 writeData ();

private:
int socketDescriptor;
protected:
Server *server;
};
#endif


myunit.cpp



#include "myunit.h"
#include <iostream>
#include <QtNetwork>
#include <QDebug> // TODO remove in production!

using namespace std;

extern QString getDateTime();
extern char * toCharP ( QString str );

myUnit::myUnit ( int socketDescriptor, Server *parent ) : QThread ( parent )
{
// initialize the unit's socket --> provided by Server::incomingConnection()
if ( !tcpSocket.setSocketDescriptor ( socketDescriptor ) )
{
emit error ( tcpSocket.error() );
return;
}
// this unit doesn't have an IMEI yet...
gotImei=false;
// initialize our imei (000000000000000)
strcpy(imei, "00000000000000");
// access to this parent server object
server=parent;
connect ( &tcpSocket, SIGNAL ( readyRead() ), this, SLOT ( incomingData() ) );
connect ( &tcpSocket, SIGNAL ( disconnected() ), this, SLOT ( disconnected() ) );
}


/**
* myUnit::~myUnit()
* Reimplementation of the QThread's destructor.
* Just to exhibit a message upon this client disconnection.
*/
myUnit::~myUnit()
{
char * dt = toCharP ( getDateTime() );
cout << dt << ": Unit [" << imei << "] disconnected." << " # active clients: " << server->nbrClients << endl;
}

/**
* void myUnit::run()
* Reimplementation of QThread::run().
* Just to exhibit a message upon a new client connection.
* The exec() begins a loop for the thread's events.
*/
void myUnit::run()
{
char * dt = toCharP ( getDateTime() );
peerAddress = toCharP ( tcpSocket.peerAddress().toString() );
cout << dt << ": Incoming connection from [" << peerAddress << "]" << " # clients: " << server->nbrClients << endl;

exec();
}

/**
* void myUnit::disconnected ();
* This function (slot) is connected to the tcpSocket disconnected() signal.
* Decrements the parent server object nbrClients member and exits the thread.
* The quit() insures a graceful exit from the thread, cleaning up child objects and associated memory.
*/
void myUnit::disconnected()
{
server->nbrClients--;
this->quit();
}

/**
* void myUnit::incomingData ();
* This function (slot) is connected to the tcpSocket readyRead() signal, so that when any data is available
* on the socket it's thrown here.
* returns the number of bytes read.
*/
void myUnit::incomingData ()
{

QByteArray unitData;
unitData=tcpSocket.readAll();
int nbrBytes = unitData.length();
cout << "Incoming data from [" << peerAddress << "] - Nbr. of bytes: " << nbrBytes;
if (!gotImei) {
cout << " - checking IMEI..." << endl;
checkImei(unitData);
}
else {
cout << " - parsing data..." << endl;
}
}

quint64 myUnit::writeData ()
{
// TODO
return 0;
}

void myUnit::checkImei (QByteArray &bData)
{
QString sData(bData);
unsigned int nBytes = sData.left(2).toUInt();
cout << "nBytes: " << nBytes << endl;
cout << "sData: " << sData.right(15).toAscii().data() << endl;
}


As you can see I'm even converting the QByteArray into a QString in checkImei...
Only if I assign sData=bData.right(15).toAscii().data() right off will I get the Imei... I'm aware that this is expected behavior since QString data is \0 terminated and the first 2 bytes of dData contain a \0 byte value.

So I wonder... If my devices output everything in binary,16bit values, how do I write a function to extract the data coming in that socket?! :confused: :frustrated:

Please help using the above code so that I can get out of this and start focusing on the real implementation...
(I really don't want to go back to PHP...)

Thanks in advance,
Pedro Doria Meunier.

pdoria
17th January 2008, 17:53
bump.

Anyone?

I've tried QDataStream and QTextStream ... this is getting insane.
It shouldn't be THIS hard to extract bytes from a bloody socket stream!
Please help.

Thank you.

jacek
17th January 2008, 18:09
First of all you have to create the socket in run() (without a parent) and you should use direct connections (instead of the default automatic ones) between the socket and the thread object. That's because the thread object lives in the GUI thread and you want to use your socket in a separate thread that will be created when you invoke start().

I don't see QDataStream anywhere in above snippet, so I can't say for sure why it causes segfaults, but most likely it has something to do with thread affinity.