I know some similar questions may have been asked already, but the answers to those I found covered very specific problems and I still haven\'t figured it out.
In my
As you said, you need to wait that your header is entirely sent, before reading it, and then read the good number of bytes and emit a signal for data availability.
Here an example (untested) :
//header file
class Peer {
//[...]
protected:
bool m_headerRead; //initialize to false
unsigned int m_size_of_data_to_read;
//[...]
};
//source file
void QPeer::sendData(QByteArray data)
{
int size = data.size();
_socket->write((const char*) &size, sizeof(int);
//use directly QIODevice::write(QByteArray)
_socket->write(data);
}
void QPeer::readData()
{
int bytes = _socket->bytesAvailable();
bool contains_enough_data = true;
while (contains_enough_data) {
if (! m_headerRead && _socket->bytesAvailable() >= sizeof(int)) {
//read header only and update m_size_of_data_to_read
m_headerRead = true;
} else if (m_headerRead && _socket->bytesAvailable >= m_size_of_data_to_read) {
//read data here
m_headerRead = false;
emit dataAvailable();
} else {
contains_enough_data = false; //wait that further data arrived
}
}
}
I worked on a project that does what you expect, see here the solution that I developed to our problems, simplified to be easier to understand:
Edited, added support to the server deal with multiple clients.
Client.h:
#include <QtCore>
#include <QtNetwork>
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QObject *parent = 0);
public slots:
bool connectToHost(QString host);
bool writeData(QByteArray data);
private:
QTcpSocket *socket;
};
Client.cpp:
#include "client.h"
static inline QByteArray IntToArray(qint32 source);
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
}
bool Client::connectToHost(QString host)
{
socket->connectToHost(host, 1024);
return socket->waitForConnected();
}
bool Client::writeData(QByteArray data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
socket->write(IntToArray(data.size())); //write size of data
socket->write(data); //write the data itself
return socket->waitForBytesWritten();
}
else
return false;
}
QByteArray IntToArray(qint32 source) //Use qint32 to ensure that the number have 4 bytes
{
//Avoid use of cast, this is the Qt way to serialize objects
QByteArray temp;
QDataStream data(&temp, QIODevice::ReadWrite);
data << source;
return temp;
}
Server.h:
#include <QtCore>
#include <QtNetwork>
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0);
signals:
void dataReceived(QByteArray);
private slots:
void newConnection();
void disconnected();
void readyRead();
private:
QTcpServer *server;
QHash<QTcpSocket*, QByteArray*> buffers; //We need a buffer to store data until block has completely received
QHash<QTcpSocket*, qint32*> sizes; //We need to store the size to verify if a block has received completely
};
Server.cpp:
#include "server.h"
static inline qint32 ArrayToInt(QByteArray source);
Server::Server(QObject *parent) : QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
qDebug() << "Listening:" << server->listen(QHostAddress::Any, 1024);
}
void Server::newConnection()
{
while (server->hasPendingConnections())
{
QTcpSocket *socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
QByteArray *buffer = new QByteArray();
qint32 *s = new qint32(0);
buffers.insert(socket, buffer);
sizes.insert(socket, s);
}
}
void Server::disconnected()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
socket->deleteLater();
delete buffer;
delete s;
}
void Server::readyRead()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
qint32 size = *s;
while (socket->bytesAvailable() > 0)
{
buffer->append(socket->readAll());
while ((size == 0 && buffer->size() >= 4) || (size > 0 && buffer->size() >= size)) //While can process data, process it
{
if (size == 0 && buffer->size() >= 4) //if size of data has received completely, then store it on our global variable
{
size = ArrayToInt(buffer->mid(0, 4));
*s = size;
buffer->remove(0, 4);
}
if (size > 0 && buffer->size() >= size) // If data has received completely, then emit our SIGNAL with the data
{
QByteArray data = buffer->mid(0, size);
buffer->remove(0, size);
size = 0;
*s = size;
emit dataReceived(data);
}
}
}
}
qint32 ArrayToInt(QByteArray source)
{
qint32 temp;
QDataStream data(&source, QIODevice::ReadWrite);
data >> temp;
return temp;
}
Note: Do not use this method to transfer large files because with this method the entire contents of the message is put inside the memory before sent and this causes a high memory usage. And because 32 bits signed INT has max value to 2,147,483,647, if your input data has a value higher than that in bytes it won't work. Take care.