Reputation: 4177
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
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
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
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:
Also note that the enum/struct/class holding the packet IDs must be identical with the client and the server.
Upvotes: 0