I understand with Tcp the protocol in Qt. There is a server, it sends a message to the client.

void Server::sendToClientMessage() { if (socket != NULL) { // Заранее размер блока неизвестен, мы не можем записать данные сразу в сокет, // так как размер блока должен быть выслан в первую очередь QByteArray arrBlock; QDataStream out(&arrBlock, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_7); // Записываем все данные блока, размер 0 out << quint16(0); out << QTime::currentTime() << ui->lineEdit_message->text(); // Вывод вашего сообщения в историю сообщений ui->textEdit_allMessage->append(QTime::currentTime().toString() + " - Ваше сообщение: " + ui->lineEdit_message->text()); // Перемещение указателя на начало блока out.device()->seek(0); // Вычисление размера блока arrSize уменьшенный на sizeof(quint16) // запись в поток out с текущей позиции, которая уже перемещена в начало блока out << quint16(arrBlock.size() - sizeof(quint16)); // Созданный блок записывается в сокет socket->write(arrBlock); ui->lineEdit_message->clear(); } else { return; } } 

He also sends the picture to the client by pushButton

 void Server::transferImage() { QBuffer buffer; QImageWriter writer(&buffer, "jpg"); writer.write(imageJPEG); QByteArray barr; QDataStream stream(&barr, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_7); stream << (quint32)buffer.size(); barr.append(buffer.data()); socket->write(barr); } 

How does the client side determine what comes up? message or picture? There are 2 connections

 // Передача сообщения клиенту connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); // Передача картинки клиенту connect(socket, SIGNAL(readyRead()), this, SLOT(receivingImage())); 

Now they work out 2 methods and this is not correct. If we transmit a picture, he tries to display the message too, although we did not transmit it, and vice versa. Because of this, none of the methods work. If you comment out one of the connections and the corresponding slot, then all is normal. Ie either a message or a picture. It would be possible to stuff it into one method and inside it can already be disassembled, but how can you define a picture inside the method or just a message?

Server Connection Method

 void Client::connectedToServer() { // Если клиент уже подключен if(socket != NULL) { // Отключить клиента клиента disconnectFromServer(); } else { socket = new QTcpSocket(this); // Подключение к серверу socket->connectToHost(ui->lineEdit_ipAddress->text(),ui->spinBox_port->value()); // оповестить пользователя что он подключён connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); //connect(sock) // Возможные ошибки при подключении connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotError(QAbstractSocket::SocketError))); // Передача сообщения клиенту connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); // Передача картинки клиенту connect(socket, SIGNAL(readyRead()), this, SLOT(receivingImage())); } } 

Receiving a message

 void Client::slotReadyRead() { // Считывание в двоичный поток данных, объект класса socket унасследованный от QIODevice QDataStream in(socket); // Так как формат постоянно изменяется, и в разных версиях он может работать по разному. // Установка формата обмена данных на версию Qt 5.7. in.setVersion(QDataStream::Qt_5_7); // Не все высланные клиентом данные могут прийти одновременно. // Сервер должен уметь получать, как весь блок целиком, так и только часть блока, // а так же и все блоки сразу. for (;;) { // Размер блока заранее неизвестен, сравниваем с нулём. if (nextBlockSize == 0) { // Если пришло меньше 2-х байт, ждём пока будет 2 байта. // Первые 2 байта это размер блока if (socket->bytesAvailable() < (int)sizeof(quint16)) { break; } // Блок получен целиком. Считываем размер блока (2 байта) in >> nextBlockSize; } // Ждём пока блок придёт полностью if (socket->bytesAvailable() < nextBlockSize) { break; } // Вот тут можно было бы определять картинка здесь или сообщение, но как? // Определяем дату и строку, они в сообщении сервера QTime time; QString str; // Считывание данных из потока в переменные in >> time >> str; ui->textEdit_allMessage->append(time.toString() + " - Сообщение от сервера: " + str); // Каждый раз обнуляем блок, для принятия следующего nextBlockSize = 0; } } 

Receiving pictures

 void Client::receivingImage() { if (nextBlockSize == 0) { QDataStream stream(socket); if (socket->bytesAvailable() < sizeof(quint32)) return; stream >> nextBlockSize; } if (nextBlockSize > socket->bytesAvailable()) return; QByteArray barr = socket->read(nextBlockSize); QBuffer buffer(&barr); buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer, "jpg"); receivingImageJPG = reader.read(); if (!receivingImageJPG.isNull()) { ui->label_reciveImageJPG->setPixmap(QPixmap::fromImage(receivingImageJPG).scaled(ui->label_reciveImageJPG->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); nextBlockSize = 0; } else { ui->label_reciveImageJPG->setText("Возникла ошибка при передачи картинки."); } } 

Here's something like this is needed. But there is a hard limit if more than 10 kilobytes arrive, we believe that this is a picture and is displayed. If less, then the message. How to recognize in advance what is stored in the QDataStream message or picture?

 void Client::slotReadyRead() { // Считывание в двоичный поток данных, объект класса socket унасследованный от QIODevice QDataStream in(socket); // Так как формат постоянно изменяется, и в разных версиях он может работать по разному. // Установка формата обмена данных на версию Qt 5.7. in.setVersion(QDataStream::Qt_5_7); // Не все высланные клиентом данные могут прийти одновременно. // Сервер должен уметь получать, как весь блок целиком, так и только часть блока, // а так же и все блоки сразу. for (;;) { // Размер блока заранее неизвестен, сравниваем с нулём. if (nextBlockSize == 0) { // Если пришло меньше 2-х байт, ждём пока будет 2 байта. // Первые 2 байта это размер блока if (socket->bytesAvailable() < (int)sizeof(quint64)) { break; } // Блок получен целиком. Считываем размер блока (2 байта) in >> nextBlockSize; } // Ждём пока блок придёт полностью if (socket->bytesAvailable() < nextBlockSize) { break; } if (nextBlockSize < 10000) { // Определяем дату и строку, они в сообщении сервера QTime time; QString str; // Считывание данных из потока в переменные in >> time >> str; ui->textEdit_allMessage->append(time.toString() + " - Сообщение от сервера: " + str); } else { QByteArray barr = socket->read(nextBlockSize); QBuffer buffer(&barr); buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer, "jpg"); receivingImageJPG = reader.read(); if (!receivingImageJPG.isNull()) { ui->label_reciveImageJPG->setPixmap(QPixmap::fromImage(receivingImageJPG).scaled(ui->label_reciveImageJPG->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { ui->label_reciveImageJPG->setText("Возникла ошибка при передачи картинки."); } } // Каждый раз обнуляем блок, для принятия следующего nextBlockSize = 0; } } 
  • one
    And if you preface some code, what goes next? - Harry
  • Not very familiar with QTcp, but as an option at the beginning, you can send an additional message, which will contain information about the type of data being transferred. - evilnw

2 answers 2

If there is no agreement between the sending and receiving parties about the nature of the content for the current session, then you can send all possible options at once. It's like a box of chocolates. It can be full, or it can be partially tasted. It seems all the formalities are met: box, ribbon with a bow. But it does not leave the feeling of hard ... bewilderment with the direct study of the contents.

Gift Class Announcement:

 class Gift { friend QDataStream &operator<<(QDataStream &stream, const Gift &gift); friend QDataStream &operator>>(QDataStream &stream, Gift &gift); public: Gift() {} virtual ~Gift() {} QString text() const {return _txt} void setText(const QString &txt) {_txt = txt;} QImage image() const {return _img;} void setImage(const QImage &img) {_img = img;} private: QString _txt; QImage _img; }; Q_DECLARE_METATYPE(Gift) 

Somewhere in the gift class implementation:

 qRegisterMetaType<Gift>("Gift"); qRegisterMetaTypeStreamOperators<Gift>("Gift"); QDataStream &operator<<(QDataStream &stream, const Gift &gift) { return stream << gift._txt << gift._img; } QDataStream &operator>>(QDataStream &stream, Gift &gift) { stream >> gift._txt >> gift._img; return stream; } 

Further, it remains only to formalize, in fact, the transfer ( _socket is an object of a class of QAbstractSocket successor):

 QByteArray SocketManager::compressData(const QVariant &var) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream << var; return qCompress(data, 9); } QVariant SocketManager::uncompressData(const QByteArray &data) { QByteArray raw_data = qUncompress(data); if(raw_data.isEmpty()) return QVariant(); QDataStream stream(raw_data); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); QVariant var; stream >> var; return var; } void SocketManager::sendMessage(const QVariant &var) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream << compressData(var); // Вставлять в поток размер блока данных необходимости нет // в том случае, если используется Qt 5.7 и выше. _socket->write(data); } void SocketManager::onReadyRead() { QDataStream stream(_socket); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); while(_socket->bytesAvailable()) { stream.startTransaction(); QByteArray data; stream >> data; // Собственно проверка данных принятого размера // для Qt 5.7 и выше. if(stream.commitTransaction() == false) return; QVariant var = uncompressData(data); if(var.isValid() && var.canConvert<Gift>()) { // Время открывать шампанское. Gift gift = var.value<Gift>(); } } } 

The object of the gift class can be filled with all types of data, you can partially, but you can not fill it at all, leaving empty, so to speak, for the greater joy of the hero of the day:

 Gift gift; gift.setText("Безвозмездно"); gift.setImage(QImage(":/gift.jpg")); 

And you can send it like this:

 manager->sendMessage(QVariant::fromValue<Gift>(gift)); 

    In a word, irrespective of Qt specifically ... When transmitting over a network, it’s something like a nameless file: there is just a stream of data. How do you know what is in it - JPEG or "War and Peace"? :) For example, by the initial bytes ("header") of the file.

    So here - use your protocol. The simplest thing is to send information about what will go now before sending information. As in the old comedy - "Pick up, I will talk with you on the phone!"

    You can even preface information not only about the type, but also, for example, about the size of the transmitted data or something else - this is how your imagination and your needs will tell.

    After receiving the title, you will know exactly what you are getting next.