Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. how to transfer QImage from QLocalServer to QLocalSocket

how to transfer QImage from QLocalServer to QLocalSocket

Scheduled Pinned Locked Moved Unsolved General and Desktop
24 Posts 3 Posters 3.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Christian EhrlicherC Offline
    Christian EhrlicherC Offline
    Christian Ehrlicher
    Lifetime Qt Champion
    wrote on last edited by
    #14

    Here a minimal, working example

    #include <QtCore>
    #include <QtNetwork>
    
    class Server : public QObject
    {
      Q_OBJECT
    public:
      Server(const QByteArray &dataToSend)
        : m_dataToSend(dataToSend)
      {
        if (!m_server.listen(QHostAddress::Any, 12345)) {
          qFatal(qPrintable("Can not listen to local socket 'TestConnection': " + m_server.errorString()));
        }
        connect(&m_server, &QTcpServer::newConnection, this, &Server::onNewConnection);
      }
    private Q_SLOTS:
      void onNewConnection()
      {
        if (auto conn = m_server.nextPendingConnection()) {
          qDebug() << "New connection";
          connect(conn, &QLocalSocket::bytesWritten, this, [this, conn](qint64 count)
          {
            // this will only work when there is excatly one connection
            m_bytesSent += count;
            if (m_bytesSent == m_dataToSend.size()) {
              conn->deleteLater();
            }
          });
          QDataStream ds(conn);
          ds << m_dataToSend;
        }
      }
    private:
      QTcpServer m_server;
      QByteArray m_dataToSend;
      qint64 m_bytesSent = 0;
    };
    
    class Client : public QObject
    {
      Q_OBJECT
    public:
      Client()
      {
        connect(&m_socket, &QLocalSocket::readyRead, this, [this]() {
          QDataStream ds(&m_socket);
          ds.startTransaction();
          ds >> m_receivedData;
          if (ds.commitTransaction()) {
            qDebug() << "Received all data:" << m_receivedData.size();
            QCoreApplication::quit();
          } else {
            qDebug() << "Waiting for more data, bytesAvailable:" << m_socket.bytesAvailable();
          }
        });
        m_socket.connectToHost(QHostAddress::LocalHost, 12345);
      }
      QByteArray receivedData() const
      {
        return m_receivedData;
      }
    private:
      QTcpSocket m_socket;
      QByteArray m_receivedData;
    };
    
    int main(int argc, char *argv[])
    {
      QCoreApplication app(argc, argv);
    
      QByteArray ba(1024 * 1024 * 10, 'b');
      Server s(ba);
      Client c;
      int ret = app.exec();
      if (c.receivedData() == ba) {
        qDebug() << "Recevied the correct data";
      } else {
        qWarning() << "Received data *DOES NOT MATCH*!";
      }
      return ret;
    }
    #include "main.moc"
    

    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
    Visit the Qt Academy at https://academy.qt.io/catalog

    JonBJ 1 Reply Last reply
    1
    • Christian EhrlicherC Christian Ehrlicher

      Here a minimal, working example

      #include <QtCore>
      #include <QtNetwork>
      
      class Server : public QObject
      {
        Q_OBJECT
      public:
        Server(const QByteArray &dataToSend)
          : m_dataToSend(dataToSend)
        {
          if (!m_server.listen(QHostAddress::Any, 12345)) {
            qFatal(qPrintable("Can not listen to local socket 'TestConnection': " + m_server.errorString()));
          }
          connect(&m_server, &QTcpServer::newConnection, this, &Server::onNewConnection);
        }
      private Q_SLOTS:
        void onNewConnection()
        {
          if (auto conn = m_server.nextPendingConnection()) {
            qDebug() << "New connection";
            connect(conn, &QLocalSocket::bytesWritten, this, [this, conn](qint64 count)
            {
              // this will only work when there is excatly one connection
              m_bytesSent += count;
              if (m_bytesSent == m_dataToSend.size()) {
                conn->deleteLater();
              }
            });
            QDataStream ds(conn);
            ds << m_dataToSend;
          }
        }
      private:
        QTcpServer m_server;
        QByteArray m_dataToSend;
        qint64 m_bytesSent = 0;
      };
      
      class Client : public QObject
      {
        Q_OBJECT
      public:
        Client()
        {
          connect(&m_socket, &QLocalSocket::readyRead, this, [this]() {
            QDataStream ds(&m_socket);
            ds.startTransaction();
            ds >> m_receivedData;
            if (ds.commitTransaction()) {
              qDebug() << "Received all data:" << m_receivedData.size();
              QCoreApplication::quit();
            } else {
              qDebug() << "Waiting for more data, bytesAvailable:" << m_socket.bytesAvailable();
            }
          });
          m_socket.connectToHost(QHostAddress::LocalHost, 12345);
        }
        QByteArray receivedData() const
        {
          return m_receivedData;
        }
      private:
        QTcpSocket m_socket;
        QByteArray m_receivedData;
      };
      
      int main(int argc, char *argv[])
      {
        QCoreApplication app(argc, argv);
      
        QByteArray ba(1024 * 1024 * 10, 'b');
        Server s(ba);
        Client c;
        int ret = app.exec();
        if (c.receivedData() == ba) {
          qDebug() << "Recevied the correct data";
        } else {
          qWarning() << "Received data *DOES NOT MATCH*!";
        }
        return ret;
      }
      #include "main.moc"
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #15

      @Christian-Ehrlicher said in how to transfer QImage from QLocalServer to QLocalSocket:

      QDataStream ds(&m_socket);

      Interesting that works, with the data stream as a local variable inside the slot lambda. I would have made its scope outside the slot, so that it lasted as long as the transaction. So the transaction cancels on slot exit, and re-examines the whole of m_socket data each time round. You did test this works against multiple readRead()s for one transaction, didn't you? :)

      Christian EhrlicherC 1 Reply Last reply
      0
      • JonBJ JonB

        @Christian-Ehrlicher said in how to transfer QImage from QLocalServer to QLocalSocket:

        QDataStream ds(&m_socket);

        Interesting that works, with the data stream as a local variable inside the slot lambda. I would have made its scope outside the slot, so that it lasted as long as the transaction. So the transaction cancels on slot exit, and re-examines the whole of m_socket data each time round. You did test this works against multiple readRead()s for one transaction, didn't you? :)

        Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by Christian Ehrlicher
        #16

        @JonB said in how to transfer QImage from QLocalServer to QLocalSocket:

        Interesting that works, with the data stream as a local variable inside the slot lambda. I would have made its scope outside the slot, so that it lasted as long as the transaction.

        I don't understand this.
        See also https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions

        "If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive."

        Also the QDataStream::commitTransaction() code is useful here:

        bool QDataStream::commitTransaction()
        {
            CHECK_STREAM_TRANSACTION_PRECOND(false)
            if (--d->transactionDepth == 0) {
                CHECK_STREAM_PRECOND(false)
        
                if (q_status == ReadPastEnd) {
                    dev->rollbackTransaction(); <-- this is what you were looking for
                    return false;
                }
                dev->commitTransaction();
            }
            return q_status == Ok;
        }
        
        

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        JonBJ 1 Reply Last reply
        0
        • Christian EhrlicherC Christian Ehrlicher

          @JonB said in how to transfer QImage from QLocalServer to QLocalSocket:

          Interesting that works, with the data stream as a local variable inside the slot lambda. I would have made its scope outside the slot, so that it lasted as long as the transaction.

          I don't understand this.
          See also https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions

          "If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive."

          Also the QDataStream::commitTransaction() code is useful here:

          bool QDataStream::commitTransaction()
          {
              CHECK_STREAM_TRANSACTION_PRECOND(false)
              if (--d->transactionDepth == 0) {
                  CHECK_STREAM_PRECOND(false)
          
                  if (q_status == ReadPastEnd) {
                      dev->rollbackTransaction(); <-- this is what you were looking for
                      return false;
                  }
                  dev->commitTransaction();
              }
              return q_status == Ok;
          }
          
          
          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #17

          @Christian-Ehrlicher said in how to transfer QImage from QLocalServer to QLocalSocket:

          I don't understand this.

          That's OK, I don't understand why you choose to write it your way either :)

          You have the QDataStream local to the slot. It can start a transaction, and then if it does not have all the data the slot exits and the QDataStream is destroyed. I don't really understand where the partial data read from the socket has been stored for re-use next time round on the next readyRead.

          I also see void QDataStream::startTransaction()

          For sequential devices, read data will be duplicated internally to allow recovery in case of incomplete reads.

          So first there is some copying which I guess must be repeated, seems wasteful.

          I understand the/your use of startTransaction/commitTransaction(). What I do not understand is having the QDataStream ds(&m_socket) in the slot, and allowing it to be destroyed when the slot cannot read all the data to end a transaction.

          OK, now I understand better: your ds >> m_receivedData; appends whatever data has been received off to a buffer.

          OK then, here is the bit I do not understand now. When the server sends a whole QByteArray to the socket via QDataStream, the QDataStream must put in something like a "byte count" in the header/start of the data stream, so that the client receiver will know that/know when the receiving transaction has read all data. Correct, or wrong?

          Then when the client receiver goes QDataStream ds(&m_socket); ds.startTransaction() does that not read the "byte count" at the head of the data received, so it will know how many total bytes the server will be sending for the QByteArray? Correct, or wrong?

          If it does work that way, if you kill off the QDataStream at the read side because reading the whole QByteArray is incomplete and you allow the QDataStream to go out of scope, when you next enter that slot and create a new QDataStream/startTransaction() how does it know how many more bytes received will end the transaction and complete the transfer of the original QByteArray from the sender?

          Christian EhrlicherC 1 Reply Last reply
          0
          • JonBJ JonB

            @Christian-Ehrlicher said in how to transfer QImage from QLocalServer to QLocalSocket:

            I don't understand this.

            That's OK, I don't understand why you choose to write it your way either :)

            You have the QDataStream local to the slot. It can start a transaction, and then if it does not have all the data the slot exits and the QDataStream is destroyed. I don't really understand where the partial data read from the socket has been stored for re-use next time round on the next readyRead.

            I also see void QDataStream::startTransaction()

            For sequential devices, read data will be duplicated internally to allow recovery in case of incomplete reads.

            So first there is some copying which I guess must be repeated, seems wasteful.

            I understand the/your use of startTransaction/commitTransaction(). What I do not understand is having the QDataStream ds(&m_socket) in the slot, and allowing it to be destroyed when the slot cannot read all the data to end a transaction.

            OK, now I understand better: your ds >> m_receivedData; appends whatever data has been received off to a buffer.

            OK then, here is the bit I do not understand now. When the server sends a whole QByteArray to the socket via QDataStream, the QDataStream must put in something like a "byte count" in the header/start of the data stream, so that the client receiver will know that/know when the receiving transaction has read all data. Correct, or wrong?

            Then when the client receiver goes QDataStream ds(&m_socket); ds.startTransaction() does that not read the "byte count" at the head of the data received, so it will know how many total bytes the server will be sending for the QByteArray? Correct, or wrong?

            If it does work that way, if you kill off the QDataStream at the read side because reading the whole QByteArray is incomplete and you allow the QDataStream to go out of scope, when you next enter that slot and create a new QDataStream/startTransaction() how does it know how many more bytes received will end the transaction and complete the transfer of the original QByteArray from the sender?

            Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by Christian Ehrlicher
            #18

            @JonB said in how to transfer QImage from QLocalServer to QLocalSocket:

            don't really understand where the partial data read from the socket has been stored for re-use next time round on the next readyRead.
            how does it know how many more bytes received will end the transaction and complete the transfer of the original QByteArray from the sender?

            In the QTcpSocket -> abort/rollbackTransaction() restores the state of the underlying QIODevice (if the IODevice support this which is imo not the case for QSerialPort but I might be wrong)

            , the QDataStream must put in something like a "byte count" in the header/start of the data stream, so that the client receiver will know that/know when the receiving transaction has read all data. Correct, or wrong?

            That's the reason why we use QDataStream here - QDataStream adds some bytes so it can be properly deserialized later on. But not much logic tbh.

            Maybe the code of QIODevice::rollbackTransaction() helps you:

            void QIODevice::rollbackTransaction()
            {
                Q_D(QIODevice);
                if (!d->transactionStarted) {
                    checkWarnMessage(this, "rollbackTransaction", "Called while no transaction in progress");
                    return;
                }
                if (!d->isSequential())   <-- here is the magic for non-sequential iodevices like e.g QTcpSocket or QLocalSocket or QFile
                    d->seekBuffer(d->transactionPos);
                d->transactionStarted = false;
                d->transactionPos = 0;
            }
            

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            1 Reply Last reply
            0
            • Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #19

              Ok, now you've got me - QTcpSocket is sequential... but my code works as expected (and as I've done multiple times iirc) - need to investigate.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              0
              • Christian EhrlicherC Offline
                Christian EhrlicherC Offline
                Christian Ehrlicher
                Lifetime Qt Champion
                wrote on last edited by
                #20

                Ok, the magic is in QIODevicePrivate::read()

                {
                    Q_Q(QIODevice);
                
                    const bool buffered = (openMode & QIODevice::Unbuffered) == 0;
                    const bool sequential = isSequential();
                    const bool keepDataInBuffer = sequential
                                                  ? peeking || transactionStarted
                                                  : peeking && buffered;
                    const qint64 savedPos = pos;
                    qint64 readSoFar = 0;
                    bool madeBufferReadsOnly = true;
                    bool deviceAtEof = false;
                    char *readPtr = data;
                    qint64 bufferPos = (sequential && transactionStarted) ? transactionPos : Q_INT64_C(0);
                        // Try reading from the buffer.
                        qint64 bufferReadChunkSize = keepDataInBuffer
                                                     ? buffer.peek(data, maxSize, bufferPos)   <-- the data is 'peeked', not read
                                                     : buffer.read(data, maxSize);
                        if (bufferReadChunkSize > 0) {
                

                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                Visit the Qt Academy at https://academy.qt.io/catalog

                JonBJ 1 Reply Last reply
                1
                • Christian EhrlicherC Christian Ehrlicher

                  Ok, the magic is in QIODevicePrivate::read()

                  {
                      Q_Q(QIODevice);
                  
                      const bool buffered = (openMode & QIODevice::Unbuffered) == 0;
                      const bool sequential = isSequential();
                      const bool keepDataInBuffer = sequential
                                                    ? peeking || transactionStarted
                                                    : peeking && buffered;
                      const qint64 savedPos = pos;
                      qint64 readSoFar = 0;
                      bool madeBufferReadsOnly = true;
                      bool deviceAtEof = false;
                      char *readPtr = data;
                      qint64 bufferPos = (sequential && transactionStarted) ? transactionPos : Q_INT64_C(0);
                          // Try reading from the buffer.
                          qint64 bufferReadChunkSize = keepDataInBuffer
                                                       ? buffer.peek(data, maxSize, bufferPos)   <-- the data is 'peeked', not read
                                                       : buffer.read(data, maxSize);
                          if (bufferReadChunkSize > 0) {
                  
                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by
                  #21

                  @Christian-Ehrlicher
                  Now you're talking about buffering to revert? The sender is sending an arbitrary sized QByteArray. The OP has an image, could be big. Let's say it's a 1GB byte array (a big image, a movie, ...). So the receiver client is going to need to buffer > 1GB of data? Is whatever buffer is being used big enough/limited?

                  Christian EhrlicherC 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @Christian-Ehrlicher
                    Now you're talking about buffering to revert? The sender is sending an arbitrary sized QByteArray. The OP has an image, could be big. Let's say it's a 1GB byte array (a big image, a movie, ...). So the receiver client is going to need to buffer > 1GB of data? Is whatever buffer is being used big enough/limited?

                    Christian EhrlicherC Offline
                    Christian EhrlicherC Offline
                    Christian Ehrlicher
                    Lifetime Qt Champion
                    wrote on last edited by Christian Ehrlicher
                    #22

                    @JonB said in how to transfer QImage from QLocalServer to QLocalSocket:

                    Is whatever buffer is being used big enough/limited?

                    2GB since it's a QByteArray with Qt5, 2^63Bytes with Qt6.

                    But even when you don't destroy the QDataStream it would be buffered in QDataStream - so no difference.
                    Sending more than 1GB with QDataStream + transaction is ... not useful. Using a simple own protocol is much better for such a task - also on the sender side since you then don't have to load the 1GB directly into the memory.

                    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                    Visit the Qt Academy at https://academy.qt.io/catalog

                    JonBJ 1 Reply Last reply
                    0
                    • Christian EhrlicherC Christian Ehrlicher

                      @JonB said in how to transfer QImage from QLocalServer to QLocalSocket:

                      Is whatever buffer is being used big enough/limited?

                      2GB since it's a QByteArray with Qt5, 2^63Bytes with Qt6.

                      But even when you don't destroy the QDataStream it would be buffered in QDataStream - so no difference.
                      Sending more than 1GB with QDataStream + transaction is ... not useful. Using a simple own protocol is much better for such a task - also on the sender side since you then don't have to load the 1GB directly into the memory.

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #23

                      @Christian-Ehrlicher
                      Exactly :)

                      But even when you don't destroy the QDataStream it would be buffered in QDataStream - so no difference.

                      Yes, true.

                      I kind of get that your code works, just intuitively I would have expected to keep the QDataStream in scope across the start/commitTransaction() calls, not local to the slot, so they operate on the same instance.

                      1 Reply Last reply
                      0
                      • E Offline
                        E Offline
                        ebrahimcoder
                        wrote on last edited by ebrahimcoder
                        #24

                        @Christian-Ehrlicher @JonB Thanks for your reply.
                        Finally I got the solutions I used QDataStream.

                        I was doing mistake at client side code was giving wrong format and size of the image

                        BEFORE
                        QImage image((uchar *)imageData.data(),252,252,QImage::Format_ARGB32);

                        AFTER
                        QImage image((uchar *)imageData.data(),640,480,QImage::Format_RGB888);

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved