Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    Solved QTcpSocket::read() reads 0 bytes and it shouldn't: why?

    General and Desktop
    5
    11
    2055
    Loading More Posts
    • 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.
    • aha_1980
      aha_1980 Lifetime Qt Champion @Bart_Vandewoestyne last edited by aha_1980

      Hi @Bart_Vandewoestyne

      1. I hope you use waitForReadyRead() in a separate thread, not in the main thread. That will most likely not work.
      2. Have you set a timeout for waitForReadyRead()? From your description, it sounds like the whole code runs faster than you expect and is only slowed down by the debug prints - and therefore you don't see the bug when you enable the debug output.

      Qt has to stay free or it will die.

      1 Reply Last reply Reply Quote 5
      • jsulm
        jsulm Lifetime Qt Champion @Bart_Vandewoestyne last edited by

        @Bart_Vandewoestyne said in QTcpSocket::read() reads 0 bytes and it shouldn't: why?:

        while (theByte != someEndFlag)

        I'm wondering what will happen if you read past these 14 bytes without getting someEndFlag? You will keep calling read(), right? And I think that's why you get bytesRead = 0 - because there is nothing more to read. This code really needs to be fixed:

        while (theByte != someEndFlag)
        {
            const auto bytesRead = read(&theByte, 1);
            if (bytesRead == 0)
               break;
            ....
        }
        

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        aha_1980 B 2 Replies Last reply Reply Quote 2
        • aha_1980
          aha_1980 Lifetime Qt Champion @jsulm last edited by

          @jsulm said in QTcpSocket::read() reads 0 bytes and it shouldn't: why?:

          I'm wondering what will happen if you read past these 14 bytes without getting someEndFlag?

          That is a very good point! Well spotted.

          Qt has to stay free or it will die.

          1 Reply Last reply Reply Quote 1
          • J.Hilk
            J.Hilk Moderators last edited by

            I'm kinda confused, why, when reading a single char, the buffer is read in a while loop?
            I mean the while loop either does not enter or breaks after the first cycle.

            Is this for compiler optimization?

            qint64 QIODevice::read(char *data, qint64 maxSize)
            {
                Q_D(QIODevice);
            
            #if defined QIODEVICE_DEBUG
                printf("%p QIODevice::read(%p, %lld), d->pos = %lld, d->buffer.size() = %lld\n",
                       this, data, maxSize, d->pos, d->buffer.size());
            #endif
            
                const bool sequential = d->isSequential();
            
                // Short-cut for getChar(), unless we need to keep the data in the buffer.
                if (maxSize == 1 && !(sequential && d->transactionStarted)) {
                    int chint;
                    while ((chint = d->buffer.getChar()) != -1) {
                        if (!sequential)
                            ++d->pos;
            
                        char c = char(uchar(chint));
                        if (c == '\r' && (d->openMode & Text))
                            continue;
                        *data = c;
            #if defined QIODEVICE_DEBUG
                        printf("%p \tread 0x%hhx (%c) returning 1 (shortcut)\n", this,
                               int(c), isprint(c) ? c : '?');
            #endif
                        if (d->buffer.isEmpty())
                            readData(data, 0);
                        return qint64(1);
                    }
                }
            
                CHECK_MAXLEN(read, qint64(-1));
                CHECK_READABLE(read, qint64(-1));
            
                const qint64 readBytes = d->read(data, maxSize);
            
            #if defined QIODEVICE_DEBUG
                printf("%p \treturning %lld, d->pos == %lld, d->buffer.size() == %lld\n", this,
                       readBytes, d->pos, d->buffer.size());
                if (readBytes > 0)
                    debugBinaryString(data - readBytes, readBytes);
            #endif
            
                return readBytes;
            }
            

            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct

            Qt Needs YOUR vote: https://bugreports.qt.io/browse/QTQAINFRA-4121


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            B 1 Reply Last reply Reply Quote 0
            • B
              Bart_Vandewoestyne @jsulm last edited by

              @jsulm said in QTcpSocket::read() reads 0 bytes and it shouldn't: why?:

              I'm wondering what will happen if you read past these 14 bytes without getting someEndFlag? You will keep calling read(), right?

              We first call waitForReadyRead again. The whole code looks more or less as follows (logging statements removed):

              char theByte = '\0';
              while (theByte != static_cast<char>(someEndFlag))
              {
              	const auto bytesRead = read(&theByte, 1);
              	if (bytesRead > 0)
              	{
              		buffer.append(theByte);
              		nbBytesRead++;
              	}
              	else  // error (-1) or no more data (0)
              	{
              		if (!waitForReadyRead(timeout))
              		{
              			buffer.clear();
              			break;
              		}
              	}
              }
              

              In the meanwhile, we think we've found the root cause of the problem. I will explain that in another reply in this discussion.

              1 Reply Last reply Reply Quote 0
              • B
                Bart_Vandewoestyne @J.Hilk last edited by

                @J.Hilk said in QTcpSocket::read() reads 0 bytes and it shouldn't: why?:

                I'm kinda confused, why, when reading a single char, the buffer is read in a while loop?
                I mean the while loop either does not enter or breaks after the first cycle.

                Is this for compiler optimization?

                I'm afraid I do not completely understand your question. I've posted more code in another reply. Does that make things more clear for you? Note also that we think we've found the root cause of the problem. I will explain that in another reply in this discussion.

                J.Hilk 1 Reply Last reply Reply Quote 0
                • J.Hilk
                  J.Hilk Moderators @Bart_Vandewoestyne last edited by

                  @Bart_Vandewoestyne
                  hi, my comment was about the actually read function read(char *data, qint64 maxSize) , I posted the code frrom the QIODevice source code, your read ist most likly based on.
                  Can't thay for sure as I don't know the base class, but I would asume it to be QIODevice. As it is the case for nearly all read/write functions in Qt :-)

                  Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct

                  Qt Needs YOUR vote: https://bugreports.qt.io/browse/QTQAINFRA-4121


                  Q: What's that?
                  A: It's blue light.
                  Q: What does it do?
                  A: It turns blue.

                  kshegunov 1 Reply Last reply Reply Quote 0
                  • B
                    Bart_Vandewoestyne last edited by Bart_Vandewoestyne

                    We think we found the root cause of our problem. After some googling, I came across this Stack Overflow post: https://stackoverflow.com/questions/16653117/why-cant-my-code-read-the-number-of-bytes-available-on-a-qtcpsocket-ideas

                    The code I've posted, is part of a function mfReadDataFromDevice() which is a member function of a FooBarSocket class that is a subclass of QTcpSocket. To check the threads in which FooBarSocket::mfReadDataFromDevice() is called, and the thread in which the read() is called (= the thread where our socket object lives), I've added some logging statements right before the read() statement (before the while loop), where I print the value of QThread::currentThread() and thread().

                    And now here comes the clue: in some of our unit tests, these threads appeared to be the same, but in the failing unit test, these threads appeared to be different.

                    On top of that, we started to experience this problem after changing a signal/slot connection to a direct function call. The signal was only connected to a single slot, and we therefore thought we could simply replace the signal/slot connection by a direct function call. But there, we overlooked that the thread in which the socket lives could be different from the thread where FooBarSocket::mfReadDataFromDevice() is called. Once we reverted that change, the unit test no longer failed.

                    So our main conclusion would be: always make sure that the socket your are reading from belongs to the same thread on which you want to do the reading.

                    My remaining questions are:

                    • Do you guys think that our conclusion is right?
                    • This seems to be something to pay attention to. Is this something that is documented for QTcpSockets? Or is it something to pay attention to for even more general things, not only when working with QTcpSockets? I would be happy to have some more URLs/references to documentation/blogposts/other websites that talk about these pitfalls.
                    • Given what we experienced/osberved, are there differences in behavior between Qt 4.8.7 (what we currently use) and Qt 5.X?

                    Kind regards,
                    Bart

                    1 Reply Last reply Reply Quote 1
                    • kshegunov
                      kshegunov Moderators @J.Hilk last edited by

                      @J.Hilk
                      I'd bet that's because of the text-mode translation for windows, see the comparison with '\r'

                      @Bart_Vandewoestyne said in QTcpSocket::read() reads 0 bytes and it shouldn't: why?:

                      • Do you guys think that our conclusion is right?

                      If your investigation in the threading is correct, which I have no reason to doubt, then yes, this does sound right.

                      • This seems to be something to pay attention to. Is this something that is documented for QTcpSockets? Or is it something to pay attention to for even more general things, not only when working with QTcpSockets? I would be happy to have some more URLs/references to documentation/blogposts/other websites that talk about these pitfalls.

                      Well, accessing objects, sockets or otherwise, from different threads has to be protected (serialized in some way). With signal slot connections Qt does that through the event loop, if you do it manually you have to use a mutex or a wait condition. That is if the class is not explicitly documented as thread safe (at the top of the class page)

                      • Given what we experienced/osberved, are there differences in behavior between Qt 4.8.7 (what we currently use) and Qt 5.X?

                      If the reason you deduced is correct, the behaviour you get is undefined, as this is a race condition. So whether or not there's a difference will vary (or not) between Qt version, compiler and OS.

                      Read and abide by the Qt Code of Conduct

                      1 Reply Last reply Reply Quote 3
                      • B
                        Bart_Vandewoestyne last edited by

                        Thanks for all the help an suggestions here. I always like it when I learn from mistakes, either my own or mistakes from others :-) Considering this closed now.

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post