curl-library
Multi Interface loop hanging with FTP-Transfer on Win32, curl-7.18.2
From: Andreas Wurf <awurf_at_adobe.com>
Date: Mon, 18 Aug 2008 12:49:54 +0100
Date: Mon, 18 Aug 2008 12:49:54 +0100
Hi all,
I have a problem with the libcurl Multi interface and
FTP transfers. My code is as follows:
----------------------------------<snip>--------------------------------
-- #define MAX_SIMULTANEOUS_CONNECTS 4 #define DEFAULT_TIMEOUT_MILLISECS 250 static HANDLE g_hMutex = 0; bool Upload( OsFileVector& files, const native_string_t& user, const native_string_t& password, const native_string_t& to_url, const native_string_t& to_path ) { if( files.empty() ) return( false ); // Initialize curl_global_init( CURL_GLOBAL_WIN32 ); g_hMutex = CreateMutex( NULL, FALSE, NULL ); CURLSH* hShare = curl_share_init(); curl_share_setopt( hShare, CURLSHOPT_LOCKFUNC, Curl_Share_Lock ); curl_share_setopt( hShare, CURLSHOPT_UNLOCKFUNC, Curl_Share_Unlock ); curl_share_setopt( hShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS ); OsFileVector::iterator file = files.begin(); // Create pool of easy handles and initialize them: CURLM* hMulti = curl_multi_init(); curl_multi_setopt( hMulti, CURLMOPT_MAXCONNECTS, MAX_SIMULTANEOUS_CONNECTS ); for( OsFileVector::size_type i = 0; i < min( MAX_SIMULTANEOUS_CONNECTS, files.size() ); i++ ) { CURL* hCurl = curl_easy_init(); curl_easy_setopt( hCurl, CURLOPT_SHARE , hShare ); curl_easy_setopt( hCurl, CURLOPT_READFUNCTION , UploadCallback ); curl_easy_setopt( hCurl, CURLOPT_USERPWD , ToMbcs( user + _T(":") + password ).c_str() ); curl_easy_setopt( hCurl, CURLOPT_DNS_USE_GLOBAL_CACHE , 0 ); curl_easy_setopt( hCurl, CURLOPT_NOSIGNAL , 1 ); curl_easy_setopt( hCurl, CURLOPT_VERBOSE , 1 ); curl_easy_setopt( hCurl, CURLOPT_FTP_CREATE_MISSING_DIRS , 1 ); curl_easy_setopt( hCurl, CURLOPT_UPLOAD , 1 ); curl_easy_setopt( hCurl, CURLOPT_URL , ToMbcs( to_url + to_path + files.GetRelativePath( (*file)->GetPathname() ) ).c_str() ); curl_easy_setopt( hCurl, CURLOPT_READDATA , *file ); curl_multi_add_handle( hMulti, hCurl ); file++; } // call curl_multi_perform() as long as it wants to get called by us: int running_handles; while( curl_multi_perform( hMulti, &running_handles ) == CURLM_CALL_MULTI_PERFORM ) ; // loop while transfers running or files left to transmit while( ( running_handles > 0 ) || ( file != files.end() ) ) { // now process waiting easy handle messages: int num_messages; CURLMsg* pMsg = curl_multi_info_read( hMulti, &num_messages ); while( pMsg ) { switch( pMsg->msg ) { case CURLMSG_DONE: std::cout << "-------> Remove handle " << pMsg->easy_handle << ", code " << pMsg->data.result << ": " << curl_easy_strerror( pMsg->data.result ) << " -> "; // populate the easy_handle with new transfer data, if there are files left to transfer: CURL* hCurl = pMsg->easy_handle; curl_multi_remove_handle( hMulti, hCurl ); if( file != files.end() ) { // better use curl_easy_reset() here and re-populate each easy handle from scratch?? curl_easy_setopt( hCurl, CURLOPT_URL , ToMbcs( to_url + to_path + files.GetRelativePath( (*file)->GetPathname() ) ).c_str() ); curl_easy_setopt( hCurl, CURLOPT_READDATA , *file ); curl_multi_add_handle( hMulti, hCurl ); file++; std::cout << "Reuse" << std::endl; } else { curl_easy_cleanup( hCurl ); std::cout << "Cleanup" << std::endl; } break; } pMsg = curl_multi_info_read( hMulti, &num_messages ); } // get curl timeout setting: long timeout; // millisecs curl_multi_timeout( hMulti, &timeout ); if( timeout < 0 ) timeout = DEFAULT_TIMEOUT_MILLISECS; // check socket action: int fdMax; fd_set fdRead, fdWrite, fdException; FD_ZERO( &fdRead ); FD_ZERO( &fdWrite ); FD_ZERO( &fdException ); curl_multi_fdset( hMulti, &fdRead, &fdWrite, &fdException, &fdMax ); if( fdMax != -1 ) { ldiv_t t = div( timeout, 1000L ); timeval tv; tv.tv_sec = t.quot; tv.tv_usec = t.rem * 1000; int select_result = select( fdMax + 1, &fdRead, &fdWrite, &fdException, &tv ); if( select_result == SOCKET_ERROR ) { std::cout << "-------> Error in select(), code " << errno << ": " << strerror( errno ) << std::endl; break; // error } } else { // Nothing to do on the sockets. Be nice to other threads and sleep for a while... Sleep( timeout ); } while( curl_multi_perform( hMulti, &running_handles ) == CURLM_CALL_MULTI_PERFORM ) ; } // Cleanup curl_multi_cleanup( hMulti ); curl_share_cleanup( hShare ); CloseHandle( g_hMutex ); curl_global_cleanup(); return( true ); } ---------------------------------</snip>-------------------------------- -- Basically the code works. It uploads the files whose pathnames are contained in the STL-Vector "files" to a specified server. It uses MAX_SIMULTANEOUS_CONNECTS easy handles for one multi handle. The problem is that *sometimes*, after transferring the last file and before sending "QUIT" to the server, the program does not exit the enclosing main (while)loop. The output of a successful run is as follows: ----------------------------------<snip>-------------------------------- -- * STATE: INIT => CONNECT handle 0x4bc698; (connection #-5000) * STATE: INIT => CONNECT handle 0x4bc8d0; (connection #-5000) * STATE: INIT => CONNECT handle 0x4bc9b8; (connection #-5000) * STATE: INIT => CONNECT handle 0x4d6040; (connection #-5000) * STATE: CONNECT => WAITRESOLVE handle 0x4bc698; (connection #0) * STATE: CONNECT => WAITRESOLVE handle 0x4bc8d0; (connection #1) * STATE: CONNECT => WAITRESOLVE handle 0x4bc9b8; (connection #2) * STATE: CONNECT => WAITRESOLVE handle 0x4d6040; (connection #3) * About to connect() to foo.bar.com port 21 (#2) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc9b8; (connection #2) * About to connect() to foo.bar.com port 21 (#3) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4d6040; (connection #3) * About to connect() to foo.bar.com port 21 (#0) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc698; (connection #0) * About to connect() to foo.bar.com port 21 (#1) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc8d0; (connection #1) * Connected to foo.bar.com (10.11.12.13) port 21 (#2) * FTP 0x4d7d40 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc9b8; (connection #2) * Connected to foo.bar.com (10.11.12.13) port 21 (#3) * FTP 0x4d8910 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4d6040; (connection #3) * Connected to foo.bar.com (10.11.12.13) port 21 (#0) * FTP 0x4d6748 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc698; (connection #0) * Connected to foo.bar.com (10.11.12.13) port 21 (#1) * FTP 0x4d7170 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc8d0; (connection #1) < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d7d40 state change from WAIT220 to USER < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d8910 state change from WAIT220 to USER < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d7d40 state change from USER to PASS < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d8910 state change from USER to PASS < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d6748 state change from WAIT220 to USER < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d6748 state change from USER to PASS < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d7170 state change from WAIT220 to USER < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d7170 state change from USER to PASS < 230 User myuser logged in. > PWD * FTP 0x4d7d40 state change from PASS to PWD < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d7d40 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc9b8; (connection #2) * DO phase starts > CWD testfiles * FTP 0x4d7d40 state change from STOP to CWD * STATE: DO => DOING handle 0x4bc9b8; (connection #2) < 250 CWD command successful > CWD awurf < 250 CWD command successful > CWD EasyMultiTest < 250 CWD command successful > CWD data < 250 CWD command successful > EPSV * FTP 0x4d7d40 state change from CWD to PASV * Connect data stream passively < 229 Entering Extended Passive Mode (|||58268|) * Trying 10.11.12.13... * Connecting to 10.11.12.13 (10.11.12.13) port 58268 * FTP 0x4d7d40 state change from PASV to STOP * DO phase is complete * STATE: DOING => DO_MORE handle 0x4bc9b8; (connection #2) * DO-MORE phase starts > TYPE I * FTP 0x4d7d40 state change from STOP to STOR_TYPE < 200 Type set to I > STOR BuildLog.htm * FTP 0x4d7d40 state change from STOR_TYPE to STOR < 150 Opening BINARY mode data connection for BuildLog.htm * FTP 0x4d7d40 state change from STOR to STOP * DO-MORE phase ends with 0 * STATE: DO_MORE => DO_DONE handle 0x4bc9b8; (connection #2) < 230 User myuser logged in. > PWD * FTP 0x4d8910 state change from PASS to PWD < 230 User myuser logged in. > PWD * FTP 0x4d6748 state change from PASS to PWD < 230 User myuser logged in. > PWD * FTP 0x4d7170 state change from PASS to PWD * STATE: DO_DONE => WAITPERFORM handle 0x4bc9b8; (connection #2) < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d8910 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4d6040; (connection #3) < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d6748 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc698; (connection #0) < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d7170 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc8d0; (connection #1) * Conn 2 recv pipe 1 inuse 0 athead 1 * STATE: WAITPERFORM => PERFORM handle 0x4bc9b8; (connection #2) * DO phase starts > CWD testfiles * FTP 0x4d8910 state change from STOP to CWD * STATE: DO => DOING handle 0x4d6040; (connection #3) * DO phase starts > CWD testfiles * FTP 0x4d6748 state change from STOP to CWD < 250 CWD command successful > CWD awurf * STATE: DO => DOING handle 0x4bc698; (connection #0) * DO phase starts > CWD testfiles * FTP 0x4d7170 state change from STOP to CWD * STATE: DO => DOING handle 0x4bc8d0; (connection #1) -------> Opening "d:\Work\NetIO-Test\EasyMultiTest\data\BuildLog.htm" -------> Read 7294 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\BuildLog.htm" < 250 CWD command successful > CWD awurf < 250 CWD command successful > CWD EasyMultiTest < 250 CWD command successful > CWD awurf -------> Closing "d:\Work\NetIO-Test\EasyMultiTest\data\BuildLog.htm" * STATE: PERFORM => DONE handle 0x4bc9b8; (connection #2) < 250 CWD command successful > CWD EasyMultiTest < 250 CWD command successful > CWD data < 250 CWD command successful > CWD EasyMultiTest * Expire cleared * Remembering we are in dir "testfiles/awurf/EasyMultiTest/data/" < 226 Transfer complete. * Connection #2 to host foo.bar.com left intact * STATE: DONE => COMPLETED handle 0x4bc9b8; (connection #-5000) ... etc...etc...etc...etc...etc...etc...etc...etc... ... last one is a large file: -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Closing "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" * STATE: PERFORM => DONE handle 0x4bc698; (connection #3) * Expire cleared * Remembering we are in dir "testfiles/awurf/EasyMultiTest/data/Debug/" < 226 Transfer complete. * Connection #3 to host foo.bar.com left intact * STATE: DONE => COMPLETED handle 0x4bc698; (connection #-5000) > QUIT * FTP 0x4d6748 state change from STOP to QUIT < 221 Goodbye. * FTP 0x4d6748 state change from QUIT to STOP * Closing connection #0 > QUIT * FTP 0x4d7170 state change from STOP to QUIT < 221 Goodbye. * FTP 0x4d7170 state change from QUIT to STOP * Closing connection #1 > QUIT * FTP 0x4d7d40 state change from STOP to QUIT < 221 Goodbye. * FTP 0x4d7d40 state change from QUIT to STOP * Closing connection #2 > QUIT * FTP 0x4d8910 state change from STOP to QUIT < 221 Goodbye. * FTP 0x4d8910 state change from QUIT to STOP * Closing connection #3 ---------------------------------</snip>-------------------------------- -- Whereas a hanging run looks like this: ----------------------------------<snip>-------------------------------- -- * STATE: INIT => CONNECT handle 0x4bc698; (connection #-5000) * STATE: INIT => CONNECT handle 0x4bc8d0; (connection #-5000) * STATE: INIT => CONNECT handle 0x4bc9b8; (connection #-5000) * STATE: INIT => CONNECT handle 0x4d6040; (connection #-5000) * STATE: CONNECT => WAITRESOLVE handle 0x4bc698; (connection #0) * STATE: CONNECT => WAITRESOLVE handle 0x4bc8d0; (connection #1) * STATE: CONNECT => WAITRESOLVE handle 0x4bc9b8; (connection #2) * STATE: CONNECT => WAITRESOLVE handle 0x4d6040; (connection #3) * About to connect() to foo.bar.com port 21 (#0) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc698; (connection #0) * About to connect() to foo.bar.com port 21 (#1) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc8d0; (connection #1) * About to connect() to foo.bar.com port 21 (#2) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4bc9b8; (connection #2) * About to connect() to foo.bar.com port 21 (#3) * Trying 10.11.12.13... * STATE: WAITRESOLVE => WAITCONNECT handle 0x4d6040; (connection #3) * Connected to foo.bar.com (10.11.12.13) port 21 (#0) * FTP 0x4d6748 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc698; (connection #0) * Connected to foo.bar.com (10.11.12.13) port 21 (#1) * FTP 0x4d7170 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc8d0; (connection #1) * Connected to foo.bar.com (10.11.12.13) port 21 (#2) * FTP 0x4d7d40 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4bc9b8; (connection #2) * Connected to foo.bar.com (10.11.12.13) port 21 (#3) * FTP 0x4d8910 state change from STOP to WAIT220 * STATE: WAITCONNECT => PROTOCONNECT handle 0x4d6040; (connection #3) < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d7d40 state change from WAIT220 to USER < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d7170 state change from WAIT220 to USER < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d6748 state change from WAIT220 to USER < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d7d40 state change from USER to PASS < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d7170 state change from USER to PASS < 230 User myuser logged in. > PWD * FTP 0x4d7170 state change from PASS to PWD < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d6748 state change from USER to PASS < 220 ProFTPD 1.3.0 Server (foo.bar.com) [::ffff:10.11.12.13] > USER myuser * FTP 0x4d8910 state change from WAIT220 to USER < 331 Password required for myuser. > PASS dapasswoid * FTP 0x4d8910 state change from USER to PASS < 230 User myuser logged in. > PWD * FTP 0x4d6748 state change from PASS to PWD < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d6748 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc698; (connection #0) < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d7170 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc8d0; (connection #1) * DO phase starts > CWD testfiles * FTP 0x4d6748 state change from STOP to CWD * STATE: DO => DOING handle 0x4bc698; (connection #0) * DO phase starts > CWD testfiles * FTP 0x4d7170 state change from STOP to CWD * STATE: DO => DOING handle 0x4bc8d0; (connection #1) < 250 CWD command successful > CWD awurf < 250 CWD command successful > CWD awurf < 250 CWD command successful > CWD EasyMultiTest < 250 CWD command successful > CWD EasyMultiTest < 250 CWD command successful > CWD data < 250 CWD command successful > CWD data < 230 User myuser logged in. > PWD * FTP 0x4d7d40 state change from PASS to PWD < 250 CWD command successful > EPSV * FTP 0x4d7170 state change from CWD to PASV * Connect data stream passively < 250 CWD command successful > EPSV * FTP 0x4d6748 state change from CWD to PASV * Connect data stream passively < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d7d40 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4bc9b8; (connection #2) * DO phase starts > CWD testfiles * FTP 0x4d7d40 state change from STOP to CWD * STATE: DO => DOING handle 0x4bc9b8; (connection #2) < 250 CWD command successful > CWD awurf < 229 Entering Extended Passive Mode (|||56776|) * Trying 10.11.12.13... * Connecting to 10.11.12.13 (10.11.12.13) port 56776 * FTP 0x4d7170 state change from PASV to STOP * DO phase is complete * STATE: DOING => DO_MORE handle 0x4bc8d0; (connection #1) * DO-MORE phase starts > TYPE I * FTP 0x4d7170 state change from STOP to STOR_TYPE < 200 Type set to I > STOR anyauthput.c * FTP 0x4d7170 state change from STOR_TYPE to STOR < 150 Opening BINARY mode data connection for anyauthput.c * FTP 0x4d7170 state change from STOR to STOP * DO-MORE phase ends with 0 * STATE: DO_MORE => DO_DONE handle 0x4bc8d0; (connection #1) < 250 CWD command successful > CWD EasyMultiTest < 230 User myuser logged in. > PWD * FTP 0x4d8910 state change from PASS to PWD < 229 Entering Extended Passive Mode (|||63290|) * Trying 10.11.12.13... * Connecting to 10.11.12.13 (10.11.12.13) port 63290 * FTP 0x4d6748 state change from PASV to STOP * DO phase is complete * STATE: DOING => DO_MORE handle 0x4bc698; (connection #0) * STATE: DO_DONE => WAITPERFORM handle 0x4bc8d0; (connection #1) * DO-MORE phase starts > TYPE I * FTP 0x4d6748 state change from STOP to STOR_TYPE < 200 Type set to I > STOR 10-at-a-time.c * FTP 0x4d6748 state change from STOR_TYPE to STOR < 150 Opening BINARY mode data connection for 10-at-a-time.c * FTP 0x4d6748 state change from STOR to STOP * DO-MORE phase ends with 0 * STATE: DO_MORE => DO_DONE handle 0x4bc698; (connection #0) * Conn 1 recv pipe 1 inuse 0 athead 1 * STATE: WAITPERFORM => PERFORM handle 0x4bc8d0; (connection #1) < 250 CWD command successful > CWD data < 257 "/" is current directory. * Entry path is '/' * FTP 0x4d8910 state change from PWD to STOP * protocol connect phase DONE * STATE: PROTOCONNECT => DO handle 0x4d6040; (connection #3) * STATE: DO_DONE => WAITPERFORM handle 0x4bc698; (connection #0) -------> Opening "d:\Work\NetIO-Test\EasyMultiTest\data\anyauthput.c" -------> Read 3823 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\anyauthput.c" < 250 CWD command successful > EPSV * FTP 0x4d7d40 state change from CWD to PASV * Connect data stream passively * DO phase starts > CWD testfiles * FTP 0x4d8910 state change from STOP to CWD * STATE: DO => DOING handle 0x4d6040; (connection #3) * Conn 0 recv pipe 1 inuse 0 athead 1 * STATE: WAITPERFORM => PERFORM handle 0x4bc698; (connection #0) -------> Closing "d:\Work\NetIO-Test\EasyMultiTest\data\anyauthput.c" * STATE: PERFORM => DONE handle 0x4bc8d0; (connection #1) -------> Opening "d:\Work\NetIO-Test\EasyMultiTest\data\10-at-a-time.c" -------> Read 4658 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\10-at-a-time.c" * Expire cleared * Remembering we are in dir "testfiles/awurf/EasyMultiTest/data/" < 226 Transfer complete. * Connection #1 to host foo.bar.com left intact * STATE: DONE => COMPLETED handle 0x4bc8d0; (connection #-5000) < 229 Entering Extended Passive Mode (|||63290|) * Trying 10.11.12.13... * Connecting to 10.11.12.13 (10.11.12.13) port 63290 * FTP 0x4d7d40 state change from PASV to STOP * DO phase is complete * STATE: DOING => DO_MORE handle 0x4bc9b8; (connection #2) < 250 CWD command successful > CWD awurf -------> Remove handle 004BCAF8, code 0: No error -> Reuse -------> Closing "d:\Work\NetIO-Test\EasyMultiTest\data\10-at-a-time.c" ... etc...etc...etc...etc...etc...etc...etc...etc... ... Last file is a big one: -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Read 16384 bytes from "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" -------> Closing "d:\Work\NetIO-Test\EasyMultiTest\data\Debug\EasyMultiTest.pch" * STATE: PERFORM => DONE handle 0x4d6328; (connection #2) * Expire cleared * Remembering we are in dir "testfiles/awurf/EasyMultiTest/data/Debug/" < 226 Transfer complete. * Connection #2 to host foo.bar.com left intact * STATE: DONE => COMPLETED handle 0x4d6328; (connection #-5000) -------> Remove handle 004C53F0, code 0: No error -> Cleanup ---------------------------------</snip>-------------------------------- -- ...and the program hangs without terminating the FTP connections with the "QUIT" command. When I break into the code at this point and do a curl_multi_dump(), I get: ----------------------------------<snip>-------------------------------- -- * Multi status: 1 handles, 1 alive handle 004B3F40, state DOING, 0 sockets ---------------------------------</snip>-------------------------------- -- ...or... ----------------------------------<snip>-------------------------------- -- * Multi status: 2 handles, 2 alive handle 004BCAF8, state DOING, 0 sockets handle 004CDA38, state DOING, 0 sockets ---------------------------------</snip>-------------------------------- -- So, I can see one or more easy handles "alive", stuck in the DOING state. My Questions are: 1) Can anybody explain to me, based on the given code, why my loop does not terminate and one or more easy handles are still "alive", stuck in the DOING state? 2) How should I modify the code to make it fully functional, i.e. to prevent hanging? 3) Since the code only hangs on occasion (approx. on each third call, may depend on server and/or network usage), could this be a timing/timeout related problem? 4) Any other suggestions? ;-) Sorry, I know this is a lengthy mail but I wanted to provide all the details you might want to know to help me with this. Your Help is very much appreciated. Thanks in advance. Regards, AndreasReceived on 2008-08-18