cURL / Mailing Lists / curl-library / Single Mail

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

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,
Andreas
Received on 2008-08-18