This project is read-only.

Uncatchable exception

Jun 14, 2013 at 12:24 AM
Edited Jun 14, 2013 at 12:25 AM
I keep getting an uncatchable exception using an older version of Net.FtpClient(I can't find the version, but I will have downloaded it about a year ago) which I assume is some exception in unmanaged code(microsoft's perhaps), perhaps having to do with threading, although that is a complete guess. The first part of the exception is:

System.IO.IOException: Unable to read data from the transport connection: 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 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)

Any ideas if this has been addressed in newer versions, or perhaps some ideas about what a person could probe into? It seems heavy cpu use may play a part in this perhaps.

I realize that sketchy internet could play a part at times, but managed code should still be able to catch this with a standard try catch block, it seems. I have added a plain catch block, without (Exception ex) to see if that may catch it.
Jun 14, 2013 at 12:53 AM
Is this exception reproducible? If so, give the latest revision I just published a shot. If not I'll need a full stack trace so I can see where in the FtpClient code it's occurring.
Jun 14, 2013 at 12:57 AM
I'll give the latest revision a try. This is one of those you can't reproduce easily, at least I haven't figured out a way.

Here is the complete exception information I have, for reference.

System.IO.IOException: Unable to read data from the transport connection: 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 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Security._SslStream.StartFrameBody(Int32 readBytes, Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartFrameHeader(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartReading(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.ReadLine()
Jun 14, 2013 at 12:59 AM
Yeah, I've seen a few issues like this through out the life of this project. I've gone through a lot of trouble to make things more robust as I've developed this project, particularly in regards to detecting interruptions in the connection. You really
need to wrap your calls in something like this: try { ftpClient.DoSomething() } catch(IOException e) { }
Jun 27, 2013 at 8:32 PM
I just want to add to this,

I've encountered a scenario in commit fbe4de737975 where in FtpClient.cs at line 1221 the FtpCommandException is not catchable.

The issue is reproducible: Upload a file to a local FTP Server (I used FileZilla) then kick the user during the upload.

What I've found in the below sample, the exception never gets caught at either opportunity.
private void UploadFile(string sourceFile, string file)
        {
            try
            {
                if (!_client.IsConnected)
                {
                    ConnectToHost();
                }
                
        <snip>

                Stream ostream;
                ostream = _client.OpenWrite(filename, FtpDataType.Binary);

                if (ostream != null)
                {
                    const int buffer = 2048;
                    byte[] contentRead = new byte[buffer];
                    int bytesRead;
                    using (FileStream fs = new FileInfo(sourceFile).OpenRead())
                    {
                        do
                        {
                            bytesRead = fs.Read(contentRead, 0, buffer);
                            ostream.Write(contentRead, 0, bytesRead);
                        } while (!(bytesRead < buffer));
                        fs.Close();
                    }
                    ostream.Close();
                }
                else
                {
                    throw new Exception();
                }
            }
            catch (FtpCommandException ftpException)
            {
                Log(this, "Failed. Will Retry", true);
            }
            catch (Exception e)
            {
                Log(this, "Failed. Will Retry", true);
                throw e;
            }
        }
Stacktrace:
System.Net.FtpClient.FtpCommandException was unhandled
  HResult=-2146233088
  Message=Kicked by Administrator
  Source=System.Net.FtpClient
  CompletionCode=421
  StackTrace:
       at System.Net.FtpClient.FtpClient.CloseDataStream(FtpDataStream stream) in E:\Libraries\system.net.ftpclient\System.Net.FtpClient\FtpClient.cs:line 1223
       at System.Net.FtpClient.FtpDataStream.Close() in E:\Libraries\system.net.ftpclient\System.Net.FtpClient\FtpDataStream.cs:line 129
       at System.Net.FtpClient.FtpSocketStream.Dispose() in E:\Libraries\system.net.ftpclient\System.Net.FtpClient\FtpSocketStream.cs:line 477
       at System.Net.FtpClient.FtpDataStream.Finalize() in E:\Libraries\system.net.ftpclient\System.Net.FtpClient\FtpDataStream.cs:line 160
  InnerException: 
Jun 27, 2013 at 8:34 PM
Thanks, I'll see if I can figure out what's going on.
Jun 27, 2013 at 9:45 PM
OK, I'm not able to trigger an uncatchable exception: Was there any inner exception that got cut off maybe?
STOR TestFile.txt
150 Connection accepted
Disposing FtpSocketStream...
421 Kicked by Administrator
Exception caught!                          <---- caught it here
System.Net.FtpClient.FtpCommandException: Kicked by Administrator
   at System.Net.FtpClient.FtpClient.CloseDataStream(FtpDataStream stream) in z:
\jptrosclair\Projects\netftp\System.Net.FtpClient\FtpClient.cs:line 1221
   at System.Net.FtpClient.FtpDataStream.Close() in z:\jptrosclair\Projects\netf
tp\System.Net.FtpClient\FtpDataStream.cs:line 129
   at System.Net.FtpClient.FtpSocketStream.Dispose() in z:\jptrosclair\Projects\
netftp\System.Net.FtpClient\FtpSocketStream.cs:line 477
   at Tests.Program.TestFileZillaKick() in z:\jptrosclair\Projects\netftp\Tests\
Program.cs:line 75
Disposing FtpClient object...
QUIT
IOException thrown and disgarded when closing control connection: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
Notice at the end an IOException was caught and discarded, it happens when the remote host closes the control connection and connectivity can't be reliably determined by the socket, that's why it's caught and discarded.

Here's the code that I wrote to test with:
static void TestFileZillaKick() {
            using (FtpClient cl = new FtpClient()) {
                cl.Host = "localhost";
                cl.Credentials = new NetworkCredential("ftptest", "ftptest");
                cl.EnableThreadSafeDataConnections = false; // also tested with Thread Safe Data channels enabled.

                if (cl.FileExists("TestFile.txt"))
                    cl.DeleteFile("TestFile.txt");

                try {
                    using (Stream s = cl.OpenWrite("TestFile.txt")) {
                        for (int i = 0; true; i++) {
                            s.WriteByte((byte)i);
                            Thread.Sleep(100);
                        }
                        
                        s.Close();
                    }
                }
                catch (FtpCommandException ex) {
                    Console.WriteLine("Exception caught!");
                    Console.WriteLine(ex.ToString());
                }
            }
        }
Jun 27, 2013 at 9:50 PM
Something you can try is to modify your code so:
if(ostream != null) {
   try {

    }
    finally {
        ostream.Close();
    }
}
So when an error does occur the stream is properly disposed. There is cleanup code that is run when data streams are closed, part of that code reads the server response which in this case would be the 421 Kick message which triggered the command exception. Garbage collection might be triggering it in your code because Close() isn't being called when a data stream had been opened. That might be why it's uncatchable.

Just a thought.
Jun 27, 2013 at 10:14 PM
I've confirmed that if the stream is not properly closed or disposed the exception is uncatchable. Make sure you call Close(), Disposed() or wrap it with a using statement so that the data stream is properly cleaned up. If you leave it to the GC the exception will (most likely) crash your application. The cleanup performed when a data stream is closed must be done, there is no way around it.
Jun 27, 2013 at 11:41 PM
Understood, thanks - your earlier response makes sense to me, there was a short delay between me kicking and the debugger catching it inside the dll - I'll modify it and wrap it in using. I know then at least it is properly being cleaned up. I seem to have fallen out of the habit of attempting using(). something I should have implemented.

Thanks for the swift response!
Jun 27, 2013 at 11:48 PM
No problem, these kinds of bugs/gotchas are the worst and the more I can do to make the project more robust the better. This kind of implementation detail is something I need clearly documented. Will work on writing something up on the documentation page tomorrow.