FXP Transfers

Using the FTP component to perform Server to Server transfers

Date Entered: 08/17/2015    Last Updated: 08/17/2015

Typically, in order to move a file from one FTP server to another, a client would need to download the file, and upload it to the second server. While this may work, it involves some unnecessary steps, and would be much simpler to send the data straight from one server to the other. FXP allows this behavior as long as the servers support it. This article will outline the necessary steps to implement FXP using either IPv4 or IPv6.

FXP transfers require the following basic steps:
  • Create a connection to two FTP servers.
  • Server 1 will use a passive data connection.
  • Specify the address and port of the data connection on Server 2 using information from the response from Server 1's EPSV command.
  • Send the STOR command on Server 1, which will be receiving the file, and RETR on Server 2, which will be sending the file.

First, implement the PITrail event to receive and parse the response from the server. This example will assume that the EPSV command was sent, so a 229 response is expected. Note that this can also be accomplished with a PASV command, but only when using IPv4.

ftpA = new Ftp(); string[] portdata; ftpA.OnPITrail += new Ftp.OnPITrailHandler(delegate(object sender, FtpPITrailEventArgs e) { Console.WriteLine("FTPA:\t" + e.Message); //The 229 response will be in this format: //<text indicating server is entering extended passive mode> (<d><d><d><tcp-port><d>) //Where <d> is the delimiter used and <tcp-port> is the port that the server is not listening on. if (e.Message.StartsWith("229")) { //Strip the string down to only what is inside the parenthesis. string portparams = e.Message.Substring(e.Message.IndexOf("(") + 1); string portparams = portparams.Remove(portparams.IndexOf(")")); //The delimiter will be the first character. char delimeter = portparams.ToCharArray(0, 1)[0]; //Split the rest of the string up using the delimeter. portdata = portparams.Split(delimeter); } });

Once the PITrail event is ready to parse the response to the EPSV command, log in, and send it.

//ftpA.Config("UseIPv6=true"); //Set UseIPv6 to true if necessary ftpA.RemoteHost = ""; ftpA.RemotePort = 21; ftpA.User = "user"; ftpA.Password = "password"; ftpA.Passive = true; ftpA.Logon(); ftpA.Command = "EPSV";

Now that the port that Server 1 is listening on is known, connect to Server 2, create an active data connection and issue the STOR and RETR commands.

//Since we determined what port ftpA is listening on, we can start the RETR on ftpB System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(FTPRetriever), portdata); ftpA.Command = "STOR test.txt\r\n"; //Store the file on Server 1 //done..disconnect Server 1 ftpA.Logoff(); static void FTPRetriever(object parameter) { string[] portdata = (string[])parameter; Ftp ftpB = new Ftp(); ftpB.OnPITrail += new Ftp.OnPITrailHandler(delegate(object sender, FtpPITrailEventArgs e) { Console.WriteLine("FTPB:\t" + e.Message); }); //ftpB.Config("UseIPv6=true"); //Set UseIPv6 to true if necessary ftpB.Timeout = 5; ftpB.RemoteHost = ""; ftpB.User = "user"; ftpB.Password = "password"; ftpB.Passive = false; ftpB.Logon(); //The server expects the EPRT command to be in the following format: //EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d> //Where <d> is the delimeter, //<net-prt> is the network protocol in which "1" indicates IPv4 and "2" indicates IPv6, //<net-addr> is the IP address of Server 1, //<tcp-port> is the port that Server 1 is listening on. if (ftpA.Config("UseIPv6") ftpB.Command = "EPRT |2|" + ftpA.RemoteHost + "|" + portdata[3] + "|"; else ftpB.Command = "EPRT |1|" + ftpA.RemoteHost + "|" + portdata[3] + "|"; ftpB.Command = "RETR test.txt"; //the file to retrieve from Server 2 //done... disconnect Server 2 ftpB.Logoff(); }

We appreciate your feedback.  If you have any questions, comments, or suggestions about this entry please contact our support team at kb@nsoftware.com.