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

          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: