I tried to send vector with class from client to server. Data is sent between sockets, but when I want to write them to the console, the server crashes
#include <QCoreApplication>
#include <QTcpSocket>
#include <QDataStream>
#include "data.h"
//client
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
std::vector< data > wektor;
data asd;
asd.setName("asd");
wektor.push_back(asd);
wektor.push_back(asd);
for (data w : wektor){
qDebug()<<w.getName();
}
quint16 rozmiarSampla = sizeof( wektor[0] );
quint16 ileSampli = wektor.size();
QTcpSocket socket;
socket.connectToHost("127.0.0.1",9999);
if( !socket.waitForConnected() )
return 1;
QDataStream stream(&socket);
QString typ("Paczka z buforem");
stream << typ << rozmiarSampla << ileSampli;
stream.writeRawData( reinterpret_cast<const char*>( wektor.data() ) , rozmiarSampla * ileSampli );
socket.flush();
socket.waitForBytesWritten();
return 0;
}
SERVER
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDataStream>
#include <QScopedPointer>
#include "data.h"
#include <iostream>
#include <string.h>
//serwer
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
std::vector< data > wektor;
QString typ;
quint16 rozmiarSampla;
quint16 ileSampli;
QTcpServer serwer;
serwer.listen(QHostAddress::AnyIPv4,9999);
if( !serwer.waitForNewConnection(30000) )
return 1;
QScopedPointer<QTcpSocket> socket{ serwer.nextPendingConnection() };
if(! socket->waitForReadyRead() )
return 1;
QDataStream stream(socket.data());
stream >> typ >> rozmiarSampla >> ileSampli;
if( rozmiarSampla == sizeof( wektor[0] ) ) {
wektor.resize(ileSampli);
stream.readRawData( reinterpret_cast<char*>( wektor.data() ) , rozmiarSampla * ileSampli );
} else {
stream.skipRawData(rozmiarSampla * ileSampli);
}
qDebug() << "Typ: " << typ;
qDebug() << "RozmiarSampla: " << rozmiarSampla;
qDebug() << "IleSampli: " << ileSampli;
qDebug()<<wektor.size();
qDebug()<< wektor[0].getName();
return a.exec();
}
DATA.H
#ifndef DATA_H
#define DATA_H
#include <QtCore>
class data
{
public:
data();
void setName(QString name);
QString getName();
private:
QString Name;
};
#endif // DATA_H
DATA.CPP
#include "data.h"
#include <QtCore>
data::data()
{
this->Name = "null";
}
void data::setName(QString name)
{
this->Name = name;
}
QString data::getName()
{
return this->Name;
}
The server crashes when trying to print the name from the data class. I don't understand why this is happening, can someone explain to me what the error is? When I tried to send a vector that consisted of data, e.g. int, everything worked fine.
As suggested by a comment, the problem here is that QString is not a trivially copyable type.
What does that mean? Well, here is a simple model of what QString looks like:
class QString {
public:
// all of the methods
private:
// QString doesn't store the data directly, it stores a pointer to the string data
Data* data;
};
reference: https://codebrowser.dev/qt5/qtbase/src/corelib/text/qstring.h.html#979
So when you do stream.writeRawData( reinterpret_cast<const char*>( wektor.data() ) , rozmiarSampla * ileSampli )
, you are copying the data contained in the QString... but what it contains is just a pointer! You are not copying the string data itself, just the pointer to the data. Of course, that pointer is nonsense on the other side of the socket.
So how can you fix this? There are lots of ways, but I would suggest specializing the QDataStream
stream operator for your type:
// In your data class header file
QDataStream& operator<<(QDataStream& stream, const data& d);
QDataStream& operator>>(QDataStream& stream, data& d);
// and in the cpp file
QDataStream& operator<<(QDataStream& stream, const data& d) {
stream << d.getName();
return stream;
}
QDataStream& operator>>(QDataStream& stream, data& d) {
QString name;
stream >> name;
d.setName(name);
return stream;
}
QDataStream
knows how to serialize QString
when given a QString
like this. What these specializations do is show QDataStream
how to serialize data
. Now with this, you can serialize your std::vector<data>
to the data stream with something like:
stream << wektor.size(); // serialize the number of instances
for (const auto& d : wektor)
stream << d; // serialize each data instance
And you can deserialize them with something like:
size_t wektorSize;
stream >> wektorSize;
std::vector<data> wektor{wektorSize};
for (auto& d : wektor)
stream >> d;