File Retrieval: Using the FTP Agent (Part II)
Example 2 is similar to Example 1, except that it uses a separate thread to retrieve the file asynchronously. The program follows these steps:
1. Creates an FTP agent object that establishes a connection with an FTP server.
2. Performs login negotiation.
3. Creates a separate thread for get() so that file retrieval can be performed asynchronously.
4. Uses the RWStreamCoupler mechanism to save the contents of the remote file in a local file.
5. Closes the data connection.
NOTE: For more information on the IOU feature and the different ways that IOU results can be redeemed with or without blocking a current thread, see Multithreading and IOUs and the ’s Guide.
NOTE: Servers and files shown in the code might not exist and are included as examples only.
Example 2 – Retrieving a file asynchronously
try {
RWFtpAgent agent("ftp.roguewave.com",
"anonymous", "me@roguewave.com"); // 1
RWTIOUResult<RWSocketPortal> sPortal =
agent.get("remote_file"); // 2
 
// Do other things here // 3
 
RWPortalIStream istrm(sPortal.redeem()); // 4
ofstream ostrm("local file"); // 5
RWStreamCoupler couple; // 6
couple(istrm, ostrm); // 7
 
bool dataClosed = agent.dataClose(); // 8
} // 9
catch (const RWxmsg& msg) { //10
cout << "ERROR: " << msg.why() << endl;
}
//1 Constructs an RWFtpAgent object that connects to an FTP server and handles the FTP login negotiation sequence.
//2 Opens a data connection to the FTP server. It returns an RWTIOUResult with an RWSocketPortal as the redeemable. It creates a new thread that handles the get() method separately. An IOU of the method is returned immediately without blocking the main thread. The RWSocketPortal can be redeemed later to complete the data portion of the protocol transfer.
//3 Handles anything else that might be of interest. The application, which is running in the main thread and has nothing to do with the agent’s get() invocation, continues to execute without waiting for the result of the get() function.
//4 Obtains an RWSocketPortal object by blocking the current thread, then builds an input stream from the socket portal.
//5 Constructs an output file stream, which is the destination of the file transfer.
//6 Creates an RWStreamCoupler object that connects an input stream to an output stream.
//7 Streams the data from the input stream to the output stream using the operator() method in RWStreamCoupler.
//8 Closes the data communication channel established by the get() method of RWFtpAgent in //2.
//9 Disconnects from the FTP server when the agent object goes out of scope.
//10 This catch clause catches all FTP package, Networking package, and Threads Module exceptions that could be thrown from within the try block because all Rogue Wave exceptions are derived from RWxmsg.
NOTE: In this example, FTP data retrieval is joined to a local file where the data is stored. This mechanism may also be used to automate copying one file to another.
The actual data transfer process, accomplished by the RWStreamCoupler mechanism, is completed within the main thread, which could cause a block when transferring large files. You may want to consider moving all code related to data transfer to a separate function, and then use the Threads Module thread-creation mechanisms to launch that function in a new thread so that the main thread does not block during the data transfer.