Я попытался отправить вектор с классом от клиента к серверу. Данные передаются между сокетами, но когда я хочу их записать в консоль сервер крашится
#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;
}
СЕРВЕР
#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();
}
ДАННЫЕ.Ч
#ifndef DATA_H
#define DATA_H
#include <QtCore>
class data
{
public:
data();
void setName(QString name);
QString getName();
private:
QString Name;
};
#endif // DATA_H
ДАННЫЕ.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;
}
Сервер падает при попытке напечатать имя из класса данных. Я не понимаю, почему это происходит, может кто-нибудь объяснить мне, в чем ошибка? Когда я попытался отправить вектор, состоящий из данных, например. инт, все работало нормально.
Вам необходимо сериализовать данные перед отправкой. Я подозреваю, что в Qt есть хорошая библиотека сериализации.
Кстати, проблема на самом деле не имеет ничего общего с qt
или даже с сокетами, а связана с правильной сериализацией объектов. Та же проблема возникла бы, если бы вы попытались прочитать/записать данные в двоичный файл, используя те же методы. Как уже упоминалось, вы должны сериализовать данные, которые представляет объект.
Двоичные функции чтения и записи буквально записывают объект. Если объект является указателем на данные и длиной данных, запись буквально записывает значение адреса указателя и значение длины. Это в значительной степени бесполезно для вас, поскольку указанные данные не записываются, а расположение данных в источнике в лучшем случае бесполезно в месте назначения. Обычно использование адреса в месте назначения фатально, потому что место назначения либо не использует это место в памяти (сбой), либо какие-то совершенно не связанные переменные используют эту память (никто не знает, что произойдет).
Не уверен, что это само по себе является обманом, но... возможно, вы захотите взглянуть на "Сериализация с помощью Qt".
Как следует из комментария, проблема здесь в том, что QString не является тривиально копируемым типом.
Что это значит? Итак, вот простая модель того, как выглядит QString:
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;
};
ссылка: https://codebrowser.dev/qt5/qtbase/src/corelib/text/qstring.h.html#979
Итак, когда вы выполняете stream.writeRawData( reinterpret_cast<const char*>( wektor.data() ) , rozmiarSampla * ileSampli )
, вы копируете данные, содержащиеся в QString... но то, что она содержит, является просто указателем! Вы не копируете сами строковые данные, а только указатель на данные. Конечно, этот указатель на другой стороне сокета — чепуха.
Итак, как вы можете это исправить? Есть много способов, но я бы посоветовал специализировать оператор потока QDataStream
для вашего типа:
// 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
знает, как сериализовать QString
, когда ему дано QString
вот так. Эти специализации показывают QDataStream
как сериализовать data
. Теперь с этим вы можете сериализовать свой std::vector<data>
в поток данных с помощью чего-то вроде:
stream << wektor.size(); // serialize the number of instances
for (const auto& d : wektor)
stream << d; // serialize each data instance
И вы можете десериализовать их с помощью чего-то вроде:
size_t wektorSize;
stream >> wektorSize;
std::vector<data> wektor{wektorSize};
for (auto& d : wektor)
stream >> d;
Я не понимаю, почему это происходит, --
QString Name;
являетсяQString
тривиально-копируемым типом? Если нет, то ничего из этогоstream.readRawData( reinterpret_cast<char*>( wektor.data() ) , rozmiarSampla * ileSampli );
не сработает. Простая причина, по которой это не сработает, заключается в том, что еслиQStrinq
содержит миллион символов, каково будет значениеrozmiarSampla * ileSampli
? Учитывая это, как вы собираетесь обрабатывать более миллиона символов, еслиrozmiarSampla * ileSampli
такой крошечный?