Как отправить вектор с классом с помощью QTcpSocket

Я попытался отправить вектор с классом от клиента к серверу. Данные передаются между сокетами, но когда я хочу их записать в консоль сервер крашится

#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;
}

Сервер падает при попытке напечатать имя из класса данных. Я не понимаю, почему это происходит, может кто-нибудь объяснить мне, в чем ошибка? Когда я попытался отправить вектор, состоящий из данных, например. инт, все работало нормально.

Я не понимаю, почему это происходит, -- QString Name; является QString тривиально-копируемым типом? Если нет, то ничего из этого stream.readRawData( reinterpret_cast<char*>( wektor.data() ) , rozmiarSampla * ileSampli ); не сработает. Простая причина, по которой это не сработает, заключается в том, что если QStrinq содержит миллион символов, каково будет значение rozmiarSampla * ileSampli ? Учитывая это, как вы собираетесь обрабатывать более миллиона символов, если rozmiarSampla * ileSampli такой крошечный?

PaulMcKenzie 05.01.2023 18:50

Вам необходимо сериализовать данные перед отправкой. Я подозреваю, что в Qt есть хорошая библиотека сериализации.

Some programmer dude 05.01.2023 18:51

Кстати, проблема на самом деле не имеет ничего общего с qt или даже с сокетами, а связана с правильной сериализацией объектов. Та же проблема возникла бы, если бы вы попытались прочитать/записать данные в двоичный файл, используя те же методы. Как уже упоминалось, вы должны сериализовать данные, которые представляет объект.

PaulMcKenzie 05.01.2023 18:55

Двоичные функции чтения и записи буквально записывают объект. Если объект является указателем на данные и длиной данных, запись буквально записывает значение адреса указателя и значение длины. Это в значительной степени бесполезно для вас, поскольку указанные данные не записываются, а расположение данных в источнике в лучшем случае бесполезно в месте назначения. Обычно использование адреса в месте назначения фатально, потому что место назначения либо не использует это место в памяти (сбой), либо какие-то совершенно не связанные переменные используют эту память (никто не знает, что произойдет).

user4581301 05.01.2023 19:25

Не уверен, что это само по себе является обманом, но... возможно, вы захотите взглянуть на "Сериализация с помощью Qt".

G.M. 05.01.2023 19:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как следует из комментария, проблема здесь в том, что 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;

Другие вопросы по теме