0

I'm a newbie in QT and C++, I'm trying to create a QTcpserver using QThreadpools so it can handle multiple clients. Multiple clients are able to connect without any issues. But I'm trying to send an image from an android phone, with a footer "IMGPNG", indicating the end of image data. Now the issue when the readyRead signal is emitted I'm tring to read all the data available data and then perform some string operation later and reconstruct the image. I'm not sure how to receive the complete image for each client and then process it accordingly.

 void VireClients::readyRead()//read ready
{

 int nsize = socket->bytesAvailable();//trying to check the available bytes

 qDebug()<< "Bytes Available" << nsize;

 while(socket->bytesAvailable() < nsize){

     QByteArray data = socket->readAll();//how to receive all the data and then process it

 }

   /*!These lines call the threadpool instance and reimplement run*/
   imageAnalysis = new VireImageAnalysis(); //creating a new instance of the QRunnable
   imageAnalysis->setAutoDelete(true);
      connect(imageAnalysis,SIGNAL(ImageAnalysisResult(int)),this,SLOT(TaskResult(int)),Qt::QueuedConnection);
   QThreadPool::globalInstance()->start(imageAnalysis);


}

Now i'm not sure how to get the data completely or save the received data in an image format. i want to know how to completely receive the image data. Please help.

Jerry
  • 424
  • 1
  • 4
  • 17
  • 1
    I would suggest using boost Asio instead for networking since there are bugs in the Qt implementation that can lead to loss of data. If you want to stick with Qt for that side of things, look at the fortune server/client examples. – Pete Nov 30 '12 at 13:07
  • 2
    Don't check `bytesAvailable`. Just read as many as it has. If you didn't get all of it, read some more. – David Schwartz Nov 30 '12 at 13:08

2 Answers2

3

A call to readAll() will not always read the complete image as it obviously cannot know the size of the image. It will only read all currently available bytes which might be less than your whole file, or more if the sender is really fast and you cannot catch up reading. The same way readyRead() only informs you that there are bytes available but not that a whole file has been received. It could be a single byte or hundreds of bytes.

Either you know the size of your image in the first place because it is always fixed or the sender has to tell the receiver the number of bytes he wants to sent.

Then you can either just ignore all readyRead() signals until bytesAvailable() matches your image size and call readAll() to read the whole image at once. Or you read whenever there are available bytes and fill up your buffer until the number of bytes read matches the bytes the receiver told you he will send.

scai
  • 20,297
  • 4
  • 56
  • 72
  • 1
    Thanks for your reply Scai. Since I dont know the size of the incoming image , I shoudl fix a buffer size and then read all available bytes. I will try and implement this now. Thanks again. – Jerry Nov 30 '12 at 14:17
  • Hi, This is what I have done, but I can't seem to get the complete image in the data variable, what am i doing wrong. /**Code**/ qint64 blockSize = 90000; if (socket->bytesAvailable() < blockSize) { return; } char* data = (char*) malloc(blockSize+100); qint64 bytesRead = socket->read(data, blockSize); qDebug() << "Data 1st in: " << data; – Jerry Nov 30 '12 at 16:01
  • Why are you using `malloc()`? This is C++ :/. You wrote that you don't know the size of the image but you still try to read *90000* bytes all the time. This cannot work. Also you have to check that `bytesRead` matches your image size. – scai Nov 30 '12 at 18:11
  • 2
    @Jerry: Your code can deadlock. Your code waits for the other side to send more bytes before it receives the ones that have already been sent. If the other side decides not to send any more bytes until you receive the ones it has already sent (which it is allowed to do) then each side will be waiting for the other. – David Schwartz Nov 30 '12 at 22:48
  • @David, thanks for your update. i think i'm kinda a lost here, could you please show me some example snippet. Meanwhile i'll keep trying different ways to implement this, and post back if i found the right solution. – Jerry Dec 01 '12 at 22:49
  • I found this in the fortuner client code, even if the device that is sending the image is not a qt application can I still use the QDatagram to receive the bytes. I have tried this but its still in small chunks rather than complete image. Here blocksize is quint16 datatype. QDataStream in(Socket); in.setVersion(QDataStream::Qt_4_0); if (blockSize == 0) { if (Socket->bytesAvailable() < (int)sizeof(quint16)) return; in >> blockSize; } if (Socket->bytesAvailable() < blockSize) return; – Jerry Dec 01 '12 at 23:05
  • 1
    @Jerry: Don't do that. Don't call `bytesAvailable`. Just read and if you don't get enough bytes, read again. Your code can deadlock. An application built on top of TCP is not allowed to refuse to receive bytes the other side has already sent until it sends more because an application *is* allowed to refuse to send more until the other side receives bytes already sent. You can't allow both kinds of waiting or each side can wait for the other. *Don't* call `bytesAvailable`. Just don't. Just read, and if you don't get enough bytes, read again. – David Schwartz Dec 02 '12 at 01:36
  • 2
    Imagine I'm trying to give you 8 pumpkins. I put 6 on the table. You say "I see those 6 pumpkins, but I'm trying to get 8. So I'll wait before I take any pumpkins off the table". But what if I say, "I'm trying to give him 8 pumpkins, but I already gave him 6 and he hasn't taken them yet. I'll wait until he takes those before I put more on the table." Now both sides are waiting for the other, and they'll wait forever. Just don't do that. Don't call `bytesAvailable` and absolutely don't decide not to read just because you can't read everything now. – David Schwartz Dec 02 '12 at 01:37
  • @DavidSchwartz, thanks for the update. The pumkin example was a real eye opener ;-). i appreciate your patience with me. I will just read the bytes and keep reading until I get all the data. Thanks again. Will post back when this works, thanks. – Jerry Dec 02 '12 at 12:20
  • @DavidSchwartz, i tried doing the below from this link: http://www.hanckmann.net/?q=node/44#comment-34579, ?*****code*******// Available are: //QTcpSocket* socket; //QBuffer* buffer = buffers.value(socket); qint64 bytes = buffer->write(socket->readAll()); // Read all the received bytes buffer->seek(buffer->pos() - bytes); // go back as many bytes as we just wrote so that it can be read QImage image; // Construct a new QImage image.loadFromData(buffer->buffer()); // Load the image from the receive buffer /****end****/ – Jerry Dec 03 '12 at 16:39
  • Then I tried this, now the entire data is in the buffer variable: char buffer[90000]; qDebug()<< "Bytes Available" << nsize; QByteArray data; data.push_back(socket->readAll()); qDebug()<< "Data in" << data.data(); if(!nsize){ return; } for(int i =0; i – Jerry Dec 03 '12 at 16:39
0

Solved saving image issue by collecting, the string in temp variable and finally, used opencv imwrite to save the image, this solved this issue:

 while(iBytesAvailable > 0 )
     {
      if(socket->isValid())
      {
       char* pzBuff = new char[iBytesAvailable];
       int iReadBytes = socket->read(pzBuff, iBytesAvailable);
       if( iReadBytes > 0 )
       {
           result1 += iReadBytes;

            str += std::string(reinterpret_cast<char const *>(pzBuff), iReadBytes);


            if(str.size() > 0){
                search = str.find("IMGPNG");

                if(search == result1-6){

                    finalID = QString::fromStdString(str);

                    Singleton_Global *strPtr = Singleton_Global::instance();
                    strPtr->setResult(finalID);

                    /*!Process the received image here*/
                    SaveImage= new VSaveImage();
                    SaveImage->setAutoDelete(false);
                    connect(SaveImage,SIGNAL(SaveImageResult(QString)),this,SLOT(TaskResult(QString)),Qt::QueuedConnection);
                    threadPool->start(SaveImage);

                }
            }

       }

Finally did the image saving on the run method -->SaveImage, @DavidSchwartz you were a great help thanks. Thanks all for your help.

Jerry
  • 424
  • 1
  • 4
  • 17