How should I handle socket exceptions on large file transfers?

+1 vote
asked Apr 22, 2011 by John Hancock (260 points)
edited May 9, 2011

I am having trouble transferring large files to certain FTP servers.

Before I get into the details, I should mention that I was able to transfer a 5GB file to my GoDaddy FTP server with no problems in 5 1/2 hours. But, I am having trouble with the servers that I am actually developing for.

On both uploads and downloads, I keep getting the following socket error: Rebex.Net.FtpException: An existing connection was forcibly closed by the remote host.

My code looks like this:

_client = new Ftp { Timeout = -1 };
_client.Options |= FtpOptions.KeepAliveDuringTransfer | FtpOptions.UseLargeBuffers;
_client.KeepAliveDuringTransferInterval = 60; // FAILS WITHOUT THIS LINE AS WELL
_client.Connect(SessionSettings.Server);
_client.Login(SessionSettings.Username, SessionSettings.Password);
_client.PutFile(SourcePath, DestinationPath);

Here's what's in the log:

2011-04-22 08:31:48.544 INFO Ftp(2) Info: Connecting to ### using Ftp 3.0.4086.0.
2011-04-22 08:31:48.544 INFO Ftp(2) Info: Using proxy none.
2011-04-22 08:31:48.653 DEBUG Ftp(2) Info: Connection succeeded.
2011-04-22 08:31:48.684 INFO Ftp(2) Response: 220 *********************
2011-04-22 08:31:48.684 INFO Ftp(2) Command: USER ###
2011-04-22 08:31:48.747 INFO Ftp(2) Response: 331 Password required for ###.
2011-04-22 08:31:48.747 INFO Ftp(2) Command: PASS *********
2011-04-22 08:31:48.793 INFO Ftp(2) Response: 230 User logged in.
2011-04-22 08:31:48.793 INFO Ftp(2) Command: FEAT
2011-04-22 08:31:48.840 INFO Ftp(2) Response: 211-Extended features supported:
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  LANG EN*
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  UTF8
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  AUTH TLS;TLS-C;SSL;TLS-P;
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  PBSZ
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  PROT C;P;
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  CCC
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  HOST
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  SIZE
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  MDTM
2011-04-22 08:31:48.840 INFO Ftp(2) Response:  REST STREAM
2011-04-22 08:31:48.840 INFO Ftp(2) Response: 211 END
2011-04-22 08:31:48.856 INFO Ftp(2) Command: OPTS UTF8 ON
2011-04-22 08:31:48.903 INFO Ftp(2) Response: 200 OPTS UTF8 command successful - UTF8 encoding now ON.
2011-04-22 08:31:48.918 DEBUG Ftp(2) Info: Starting data transfer.
2011-04-22 08:31:48.918 INFO Ftp(2) Command: TYPE I
2011-04-22 08:31:48.965 INFO Ftp(2) Response: 200 Type set to I.
2011-04-22 08:31:48.965 INFO Ftp(2) Command: PASV
2011-04-22 08:31:49.027 INFO Ftp(2) Response: 227 Entering Passive Mode (38,107,151,16,208,30).
2011-04-22 08:31:49.027 DEBUG Ftp(2) Info: Establishing data connection to 38.107.151.16:53278.
2011-04-22 08:31:49.105 INFO Ftp(2) Command: STOR //TEST/5gigs-5.txt
2011-04-22 08:31:49.168 INFO Ftp(2) Response: 125 Data connection already open; Transfer starting.
2011-04-22 08:32:49.556 INFO Ftp(2) Command: NOOP
2011-04-22 08:33:45.450 DEBUG Ftp(2) Info: Data connection closed: System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at Rebex.Net.ProxySocket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at wWGvS.AayBuI.KsZBuZ(Byte[] , Int32 , Int32 )
2011-04-22 08:33:45.450 DEBUG Ftp(2) Info: Waiting for data transfer ending message.
2011-04-22 08:33:45.466 DEBUG Ftp(2) Info: Control connection failed, closing current data connection.
2011-04-22 08:33:45.497 ERROR Ftp(2) Info: Error while writing data: Rebex.Net.FtpException: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at Rebex.Net.ProxySocket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at wWGvS.AgXIfM.BMJzdYZ(Byte[] , Int32 )
   at wWGvS.AgXIfM.BbLKfs(String& , Int32 )
   at wWGvS.AgXIfM.KsZclZ(Int32 )
   at wWGvS.AgXIfM.KsZclZ()
   at Rebex.Net.Ftp.cgrDQE(Int32 )
   --- End of inner exception stack trace ---
   at Rebex.Net.Ftp.cgrDQE(Int32 )
   at Rebex.Net.Ftp.AwjivcZ(AoLBks , FtpResponse , String , Int64 , String , Boolean )
   at wWGvS.AayBuI.KsZBuZ(Byte[] , Int32 , Int32 )
   at wWGvS.AayBuI.Write(Byte[] buffer, Int32 offset, Int32 count)
2011-04-22 08:33:45.497 ERROR Ftp(2) Info: Rebex.Net.FtpException: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at Rebex.Net.ProxySocket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at wWGvS.AgXIfM.BMJzdYZ(Byte[] , Int32 )
   at wWGvS.AgXIfM.BbLKfs(String& , Int32 )
   at wWGvS.AgXIfM.KsZclZ(Int32 )
   at wWGvS.AgXIfM.KsZclZ()
   at Rebex.Net.Ftp.cgrDQE(Int32 )
   --- End of inner exception stack trace ---
   at Rebex.Net.Ftp.cgrDQE(Int32 )
   at Rebex.Net.Ftp.AwjivcZ(AoLBks , FtpResponse , String , Int64 , String , Boolean )
   at wWGvS.AayBuI.KsZBuZ(Byte[] , Int32 , Int32 )
   at wWGvS.AayBuI.Write(Byte[] buffer, Int32 offset, Int32 count)
   at wWGvS.bsWOwAZ.Write(Byte[] buffer, Int32 offset, Int32 count)
   at Rebex.Net.Ftp.bGLizE(String , String , Stream , Int64 , Int64 )

I tried transferring the same file to the same server using FileZilla. I got the following error:

Error:      Disconnected from server: ECONNABORTED - Connection aborted

HOWEVER, the transfer did not stop. I'm wondering how FileZilla handled that error. Does anybody have any ideas. The only thing that I can think to try is the following:

1.  Start transfer
2.  Wait for and catch exception
3.  If exception is of type SocketException do the following:
  a.      Reconnect to the server
    i.      If this fails… go to 4b
  b.      Resume transfer from spot of failure
  c.      Go to 2
4.  If the exception is of any other type
  a.      Disconnect
  b.      Present user with error message
Applies to: Rebex FTP/SSL

1 Answer

0 votes
answered Apr 22, 2011 by Lukas Pokorny (86,990 points)
edited May 9, 2011

You mostly answered your questions yourself - trying to resume the transfer is the best thing you can do. Just catch FtpException instead of SocketException, because that's what is actually thrown by the Ftp object. Once you catch it, check its Status property and/or InnerException to see whether this is the error you are looking for.

It is quite possible that FileZilla Client does the same thing. If you would like to know, look into FileZilla Client's log, it should be easily possible to see what was going on.

commented Apr 22, 2011 by John Hancock (260 points)
edited Apr 22, 2011

OK, now I do this:

catch correct exception 
_client.Connect(...);
_client.Login(..., ...);
long offset = _client.GetFileSize(DestinationPath);
Stream localStream = File.OpenRead(SourcePath);
localStream.Seek(offset , SeekOrigin.Begin);
_client.PutFile(_localStream, DestinationPath, offset, -1);

BUT, when I call GetFileSize(), it returns 0. I'm guessing that when the exception happens, the server may be holding onto the destination file. Any ideas?

Also, is it enough just to call Connect and Login with the existing Ftp object? Or do I need to reinstantiate?

Thanks

commented Apr 26, 2011 by Lukas Pokorny (86,990 points)
edited Apr 26, 2011

Once you call Dispose on an existing Ftp object, it will become unusable and you will have to reinstantiate in order to Connect and Login again.

If GetFileSize() returns 0, then either the server is holding onto the destination file or it discarded the data you uploaded (some versions of MS IIS FTP have been observed to do that). If the server is holding onto the destination file (it might not even be aware that the connection was closed), you will have to wait a minute or two before resume becomes possible.

commented Apr 26, 2011 by John Hancock (260 points)
edited Apr 26, 2011

I was wrong about the 0 file size... the remote file actually isn't present. So, after I get the SocketException, the server must be removing the partial file.

commented Apr 27, 2011 by Lukas Pokorny (86,990 points)
edited Apr 27, 2011

That looks like it is in fact removing the file, which would be unfortunate. Have you checked the FileZilla log yet to see how it handles the error? Is it re-uploading the whole file as well?

commented May 6, 2011 by John Hancock (260 points)
edited May 6, 2011

Here is the important lines from the filezilla log:

Error: File transfer failed after transferring 734,593,024 bytes in 3607 seconds Status: Connecting to 38.107.151.16:21... Status: Connection established, waiting for welcome message...

...

Command: PASV Response: 227 Entering Passive Mode (38,107,151,16,255,45). Command: REST 734593024 Response: 350 Restarting at 734593024. Command: STOR 09 PITT K vs ND 11.14.09.avi Response: 150 Opening BINARY mode data connection.

If the issue is, in fact, that the server is removing the file, why is filezilla able to continue?

commented May 9, 2011 by Lukas Pokorny (86,990 points)
edited May 9, 2011

That is strange indeed. Both Filezilla and Rebex FTP use the same well-defined protocol (FTP) and I am not aware of any reason for this different behavior. Would it be possible to post a bigger chunk of Filezilla log? We might spot some difference in there.

...