2016-08-26 2 views
0

Я хотел бы отправить и получить структуру с использованием QTCPSocket.Как получить структуру через QTcpSocket?

Система имеет центральный сервер Socket и множество клиентских приложений Qt. Каждый клиент отправляет структуру, которая передается остальным центральным сервером сокета.

Connector расширяет QTcpSocket

это мой метод для подключения

void Connector::Open(QString address, int port) // It works! 
{ 
    connectToHost(address, port); 
    this->onWaiting(); 

    connect(this, SIGNAL(connected()), this, SLOT(connected())); 
    connect(this, SIGNAL(disconnected()), this, SLOT(disconnected())); 
    connect(this, SIGNAL(readyRead()), this, SLOT(readyRead())); 
    connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64))); 
} 

Это мой метод для отправки данных

void Connector::SendData(const void* data,qint64 len) // It works! 
{ 
    writeData((char*)data,len); 
    waitForBytesWritten(); 
} 

это для получения

void Connector::readyRead() // Don't work 
{ 
    Information n; //My struct 
    memcpy(&n, data.data(), data.size()); 
    onInformationReceived(n); 
} 

Но полученная информация всегда недействительна. Это верно?

+0

Какие данные вы отправляете? – purplepsycho

ответ

0

Есть некоторые проблемы:

  1. readyRead() слота может использоваться с любым количеством байт, доступных для чтения. Единственная гарантия, что у вас есть, это больше, чем нулевые байты, хотя на практике нет оснований рассчитывать на это. Вы должны проверить bytesAvailable. Это может быть один байт. Это может быть мегабайт.

    См., Например, this answer и that answer для описания, и that one для кода.

    В самом минимуме, вам нужно:

    void Connector::readyRead() { 
        if (bytesAvailable() < sizeof(Information)) return; 
        Information info; 
        auto const data = read(sizeof(Information)); 
        memcpy(&info, data.constData(), data.size()); 
        onInformationReceived(info); 
    } 
    
  2. Отправка struct как непрозрачный двоичном над проводом не будет работать. Нет никаких гарантий, что принимающая сторона будет выстраивать структуру таким же образом. Вы даже не можете знать, что такое двоичный формат данных, если вы не ссылаетесь на свою документацию по компилятору ABI.

    См., Например, this answer, как это сделать правильно.

    В самом минимуме, вы должны реализовать QDataStream потоковые операторы для Information, а затем использовать его на обоих концах соединения:

    QDataStream & operator<<(QDataStream &, const Information &); 
    QDataStream & operator>>(QDataStream &, Information &); 
    
    void Connector::send(const Information & info) { 
        auto dev = this; // We're a QIODevice :(
        QDataStream stream{dev}; 
        stream << info; 
        dev.waitForBytesWritten(); // this is superbly bad! 
    } 
    
    void Connector::readyRead() { 
        auto dev = this; // We're a QIODevice :(
        QDataStream stream{dev}; 
        stream.startTransaction(); 
        Information info; 
        dev >> info; 
        if (! stream.commitTransaction()) return; 
        onInformationReceived(info); 
    } 
    
  3. Вы не должны быть расширение QTcpSocket. Ваш класс должен быть QObject, который имеет значение QTcpSocket.

  4. Вы никогда не должны блокироваться.

Таким образом, фиксированный класс Connector может выглядеть следующим образом. Опять же, this answer описывает, как правильно использовать QDataStream, чтобы учесть совместимость с будущими обновлениями вашего кода. Но также см. this answer о том, как использовать транзакции чтения в Qt 5.7's QDataStream.

class Connector : public QObject { 
    Q_OBJECT 
    QTcpDevice m_dev; 
    QDataStream m_str{&m_dev}; 
    void onReadyRead() { 
    m_str.startTransaction(); 
    Information info; 
    m_str >> info; 
    if (! stream.commitTransaction()) return; 
    onInformationReceived(info); 
    } 
    void onBytesWritten() { 
    if (m_dev.bytesToWrite() == 0) 
     emit allDataSent(); 
    } 
    void onInformationReceived(const Information &); 
public: 
    Connector(const QString & address, int port, QObject * parent = nullptr) : 
    QObject{parent} 
    { 
    connect(&m_dev, &QAbstractSocket::connected, this, &Connector::connected); 
    connect(&m_dev, &QAbstractSocket::disconnected, this, &Connector::disconnected); 
    connect(&m_dev, &QIODevice::readyRead, this, onReadyRead); 
    connect(&m_dev, &QIODevice::bytesWritten, this, onBytesWritten); 
    m_dev.connect(address, port); 
    } 
    void send(const Information & info) { 
    m_str << info; 
    } 
    Q_SIGNAL void connected(); 
    Q_SIGNAL void disconnected(); 
    Q_SIGNAL void allDataSent(); 
} 
Смежные вопросы