QTcpSocket how to receive different type of serialized objects in sequential ?
-
current problem
I write a class inherit form qtcpserver .this server will handle many clients , I override the method
incommingConnection
. if there comes new client , i will start a thread to handle it .but each client connect to server will send a
config object
first , so the server will receive the serialized config object first ,then return some info to client .then client will sendserialized realtime infor object
to server periodically . in every thread has a pointer to qtcpsocket ,connect it toreadyread
functionMy question is how should i do in
readyread
function that i can get the qdatastream into property serialized object ?because first receive isconfig object
, then comes the periodicallyrealtime info object
. -
Think of a QTcpClient as of a binary file. Whatever you have to write to a binary file in order to read it properly later on is also required here.
There are many ways to do this. Most popular is to send before a short record or sequence of bytes to identify what comes next.
-
thanks for your answer ,but i don't get your point . As you say send some sequence bytes to identify what come next ,so what is the property way to do in that function ?
//connect function connect(tcpSocket, &QIODevice::readyRead, this, &FortuneThread::getInfo); //get info void FortuneThread::getInfo() { in.startTransaction(); //what should i do here to judge what serialized object come in ? //if some sequential bytes to identify ,like string //just like the flowing code ? in>>identifyString ; switch(identifyString ) { //some handle } //but how to handle if comes in is serialized object ? if (!in.commitTransaction()) return; }
-
The more involved way is to treat the server as a state machine (http://doc.qt.io/qt-5/statemachine-api.html) so it changes state after it receives the config and is ready to recieve other data.
The more dirty way is to define an enum with the type of transmission and append it at the beginning of the message.
For example:
enum TransmissionType :qint32 { sendingConfig = 8 // (a random number, I like 8, leave 0 for errors or special cases) , sendingRealtimeInfo }
then every client before sending the actual data sends out one of those enum values (as a 32bit integer) so that when the server receives some data, it first reads this number to decide what kind of data it should read
-
@VRonin
your answer is really helpful . but i still have not fully got your point .you say send the enum values , it's include realtimeInfo object in it ?if send enum value first ,then comes the serialized object ,so how to determine its enum or object ?
void FortuneThread::getInfo() { in.startTransaction(); //do like this ? in>>enmu_value ; switch (enmu_value) { in>>object; } if (!in.commitTransaction()) return; }
-
Untested code!!!
[I do not use transaction but the old data size header, you should use transactions]
sender (assume client)
void Client::sendConfig(const ConfigClass& val) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out << static_cast<qint32>(0) //you can skip this if you use transactions << static_cast<qint32>(TransmissionType::sendingConfig) << val ; out.device()->seek(0); //you can skip this if you use transactions out << static_cast<qint32>(block.size() - sizeof(qint32)); //you can skip this if you use transactions m_tcpSocket->write(block); } void Client::sendRealtimeInfo(const RealtimeInfo& val) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out << static_cast<qint32>(0) //you can skip this if you use transactions << static_cast<qint32>(TransmissionType::sendingRealtimeInfo) << val ; out.device()->seek(0); //you can skip this if you use transactions out << static_cast<qint32>(block.size() - sizeof(qint32)); //you can skip this if you use transactions m_tcpSocket->write(block); }
receiver (assume server)
// qint32 m_nextBlockSize; // connect(m_tcpSocket, &QTcpSocket::readyRead, this, &Server::ReadTransmission); void Server::ReadTransmission() { QDataStream incom(m_tcpSocket); qint32 RequestType; for (;;) { //from here if (m_nextBlockSize == 0) { if (m_tcpSocket->bytesAvailable() < sizeof(qint32)) break; incom >> m_nextBlockSize; } if (m_tcpSocket->bytesAvailable() < m_nextBlockSize) break; //to here can be replaced using transactions incom >> RequestType; switch (RequestType) { case TransmissionType::sendingConfig:{ ConfigClass val; incom >> val; // do something with the config } break; case TransmissionType::sendingRealtimeInfo:{ RealtimeInfo val; incom >> val; // do something with the realtime info } break; } m_nextBlockSize = 0; } }
-
@VRonin
thanks very much , now i have now your idea .Use a qint32 value as enum value ,put it in qdatastream first ,then put needed data in the same qdatastream .i almost forget that one stream can read in more than one type value .thank your very much again .