chrisrhyno2003
chrisrhyno2003

Reputation: 4177

How to read next packet in Qt

My client setup is as follows: The client first sends a packet which contains the length of the actual data to be read from the socket. The client then sends the actual data itself. ( I am pre-pending the data packet length before I actually send the data).

I want to know how can I read the next packet in Qt Creator. I get an error saying No such slot found: MyThread::readPacket (int). What am I doing wrong ?

(Ignore the part if I am reading the wrong number of bytes)

My server side code is as Follows:

#include "thread.h"
#include <QDebug>
#include "DataSetProtos.pb.h"
MyThread::MyThread(qintptr ID, QObject *parent) :
QThread(parent)
{
    this->socketDescriptor = ID;
}

void MyThread::run()
{
// thread starts here
    qDebug() << " Thread started";

    socket = new QTcpSocket();

// set the ID
  if(!socket->setSocketDescriptor(this->socketDescriptor))
{
    // something's wrong, we just emit a signal
    emit error(socket->error());
    return;
}

// connect socket and signal
// note - Qt::DirectConnection is used because it's multithreaded
//        This makes the slot to be invoked immediately, when the signal is emitted.

connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));

// We'll have multiple clients, we want to know which is which
qDebug() << socketDescriptor << " Client connected";

// make this thread a loop,
// thread will stay alive so that signal/slot to function properly
// not dropped out in the middle when thread dies

exec();
}

void MyThread::readyRead()
{
// get the information
char* buffer = new char[SIZE];
int iResult= socket->read(buffer, SIZE);

connect(socket,SIGNAL(readyRead()), this,     SLOT(readPacket(iResult)), Qt::DirectConnection);
}

void MyThread::readPacket(int bufferLength)
{
char *buffer = new char[bufferLength];
int iResult = socket->read(buffer, bufferLength);
printf("\n Read 2 : %d", iResult);

}

void MyThread::Print(Visualization::PacketData packet)
{
    printf("\n\nPacket Name : %s", packet.name());
    printf("\n Packet length : %d ", packet.length());
}

void MyThread::disconnected()
{
qDebug() << socketDescriptor << " Disconnected";


    socket->deleteLater();
exit(0);
}

My thread.h file is as follows:

#ifndef THREAD_H
#define THREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>
#include "DataSetProtos.pb.h"

#define SIZE 500
class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(qintptr ID, QObject *parent = 0);

void run();

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

public slots:
void readyRead();
void readPacket( int bufferLength);
void disconnected();
void Print( Visualization::PacketData packet );

private:
QTcpSocket *socket;
qintptr socketDescriptor;
};

#endif // THREAD_H

Upvotes: 1

Views: 1908

Answers (3)

ScottG
ScottG

Reputation: 793

As MrPickles noted, you are making it very hard on yourself. However, you don't need to go all the way to changing your data format. You just need to stop trying to use threads when they aren't necessary. Almost nothing in Qt requires the use of threads, except for heavy data processing or something along those lines. Definitely not basic network I/O.

I have an application that is communicating with message-length prefixed protobufs. Here is the gist of my onReadyRead implementation, with some details changed and unnecessary stuff stripped out:

void ProtobufMessageReceiver::onReadyRead()
{
    //Some maximum size of a message to protect from malformed
    //packets or bugs. This value should be above the size of the largest
    //protobuf message you ever expect to receive.
    static const uint32_t MAX_ALLOWED_SIZE = 1024 * 1024; //1MB

    uint32_t msg_size;
    while ( m_socket->bytesAvailable() >= sizeof(msg_size) )
    {
        m_socket->peek(reinterpret_cast<char*>(&msg_size), sizeof(msg_size));

        //OK, we have read in a 32-bit message size, now verify that
        //it is valid before we try to read that many bytes on the socket

        if ( msg_size > MAX_ALLOWED_SIZE )
        {
            //You might throw an exception here or do some other error
            //handling. Like most Qt code, though, this app doesn't
            //use exceptions, so we handle a malformed packet by just
            //closing the socket. In a real app you'd probably at least
            //want to do some logging here, as this should be a very
            //abnormal case and signifies a bug on the sending end.
            m_socket->close();
            break;
        }

        //Now check to see if we have that many bytes available to read in.
        //If we do, we have at least one full message waiting to process, if not,
        //we'll process the message on the next call to onReadyRead.
        if ( m_socket->bytesAvailable() >= sizeof(msgSize) + msgSize )
        {
            m_socket->read(reinterpret_cast<char*>(&msg_size), sizeof(msg_size));

            QScopedArrayPointer<char> buffer(new char[msgSize]);
            m_socket->read(&buffer[0], msgSize);
            processMessage(buffer, msgSize);
        }
        else
        {
            break;
        }
    }
}

The processMessage function will now always be handed a single, fully valid protobuf stream that can be decoded using parseFromArray. (In this example, we'd probably be using a protobuf Union Type to interleave several different types in one stream). Note that because you are not guaranteed that onReadyRead will be called exactly once for each protobuf message, its very important to have that while loop in there. This is really the case whether you are using Qt or some other way of working with sockets, since TCP is just a stream of bytes and doesn't demarcate message boundaries. After all, that is the whole reason we even have to do the length-prefix thing in the first place.

Upvotes: 2

Amol Saindane
Amol Saindane

Reputation: 1598

when we use signal and slot in QT then we need to be careful about parameters. Below connect won't work as, your signal is emitted without parameter but you are giving int as parameter in SLOT.

connect(socket,SIGNAL(readyRead()), this, SLOT(readPacket(iResult)), Qt::DirectConnection);

As per Qt Documentation

The signals and slots mechanism is type safe: The signature of a signal must match the signature of the receiving slot.

You can connect signal and slot as below :

connect(&socket, SIGNAL(readyRead()),this, SLOT(readData()));

And your readData method will be like as below

void MyThread :: readData()
{    
    QString readCurData = socket.readAll();
    qDebug() << readCurData;           
}

If you read socket data like this then you don't need to send number of bytes from client as well. Hope this work for you.

Upvotes: 1

MrPickles
MrPickles

Reputation: 1435

You are making it very hard on yourself. Use JSON objects to send/parses packets and thank me later.

They're well supported in Qt. No reason not to use them, as far as I can see.

No need to send the length of the packets. Your protocol should be something as follows:

  1. Client sends JSON object with an ID (not the length) and the data.
  2. Server catches the object and server's ready read function passes JSON object to a handle request function.
  3. Handle request function is nothing but a switch statement that handles the packet ID.
  4. Function handling that ID is called from switch statement.
  5. Parsing the data from the JSON object is mind-blowingly easy.
  6. Vice versa procedure when server wants to send client something.

Also note that the enum/struct/class holding the packet IDs must be identical with the client and the server.

Upvotes: 0

Related Questions