Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-19409

QFtp hangs because FD_CLOSE event was lost

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.0.0
    • 4.7.2
    • Network: FTP
    • None
    • windows 7
    • b8453b6fe3552cdfe32c726f87bb30d897c679b0

    Description

      FTP data transfer hangs sometimes during a GET command, typically the data has been transferred but the commandFinished() signal is not emitted.

      Reason of this behaviour:

      1. In the Qt event loop (qt_internal_proc() in qeventdispatcher_win.cpp) FD_ACCEPT, FD_READ, and FD_CLOSE messages are sent as QEvent::SockAct to the QSocketNotifier instance which has been registered for reading. In the case of QFtp this is QReadNotifier (implemented in qnativesocketengine.cpp). This means the QSocket* implementation cannot distiguish between FD_READ and FD_CLOSE messages.

      2. The message is sent via QReadNotifier::event(), QAbstractSocketEngine::readNotification(), and QAbstractSocketPrivate::canReadNotification(). Finally, QNativeSocketEngine::read() closes the socket if 0 bytes were read and if it's a tcp socket. This works as long as a) the socket is not emtpy while FD_READ messages are processed, and b) the socket is empty when the final FD_CLOSE message is processed.

      3. The documentation of WSAAsyncSelect(), see http://msdn.microsoft.com/en-us/library/ms741540(v=vs.85).aspx is particularly unclear about this point but it states: "FD_CLOSE should only be posted after all data is read from a socket, but an application should check for remaining data upon receipt of FD_CLOSE to avoid any possibility of losing data." Some discussion about this behaviour can also be found in the internet.

      4. The same documentation states: an FD_READ message is sent "... After recv or recvfrom is called, with or without MSG_PEEK), if data is still available to receive", i.e. when data is left on the socket after any read operation.

      5. This is the case we ran into: FD_CLOSE is processed (sometimes) way BEFORE the last FD_READ messages, and while the socket still has bytes available.

      Example: 16400 bytes are available on the socket, one FD_READ and one FD_CLOSE message are pending:

      a) Good case:

      • FD_READ triggers QNativeSocketEngine::read() to read 8192 bytes, 8208 bytes left, a new FD_READ is sent
      • It doesn't matter whether the 2nd FD_READ or the FD_CLOSE is processed first because Qt doesn't distiguish them: again 8192 bytes are read, 16 bytes are left, a new FD_READ is triggered.
      • 3rd FD_READ or the FD_CLOSE is processed: 8 bytes read, 0 left, no further FD_READ is sent.
      • the final message is processed: 0 bytes read, Qt closes the socket

      b) Bad case:

      • FD_CLOSE is processed before FD_READ. In that case QNativeSocketEngine::read() will read 8192 bytes for the FD_CLOSE message, 8208 bytes left, a new FD_READ is sent.
      • 1st FD_READ is processed: 8192 bytes read, 16 bytes left, a new FD_READ is sent.
      • 2nd FD_READ is processed: 16 bytes read, 0 bytes left, no further FD_READ is sent.
      • the socket will never be closed (and QFtp hangs) because no message will ever arrive for this socket

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-19409
          # Subject Branch Project Status CR V

          Activity

            People

              xcm Martin Petersson (Inactive)
              kamartti Katja Marttila
              Votes:
              4 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes