17

I'm here because I have a problem while downloading some files through ftp protocol. It's weird because it occurs occasionally and even for the same single file.

Just a precision: I'm downloading very large files (from 500 Mo to 30Go)

Here are the kind of Exceptions returned by my function : (sorry it's in french)

System.Net.WebException: La connexion sous-jacente a été fermée : Une erreur inattendue s'est produite lors de la réception. à System.Net.FtpWebRequest.CheckError() à System.Net.FtpWebRequest.SyncRequestCallback(Object obj) à System.IO.Stream.Close() à System.Net.ConnectionPool.Destroy(PooledStream pooledStream) à System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse) à System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage) à System.Net.FtpWebRequest.SyncRequestCallback(Object obj) à System.Net.CommandStream.Abort(Exception e) à System.Net.CommandStream.CheckContinuePipeline() à System.Net.FtpDataStream.System.Net.ICloseEx.CloseEx(CloseExState closeState) à System.Net.FtpDataStream.Dispose(Boolean disposing) à System.IO.Stream.Close() à UtilityClasses.FTP.Download(String srcDirectoryPath, String file, String destDirectoryPath)

Here is the code used to download :

the download methods :

public Dictionary<string, object> Download(string srcDirectoryPath, string file, string destDirectoryPath, int attemptLimitNb, int delay)
    {
        int attemptNb = 0;
        bool downloadFailed;
        Dictionary<string, object> result = new Dictionary<string,object>();

        do
        { 
            attemptNb++;
            result = Download(srcDirectoryPath, file, destDirectoryPath);
            downloadFailed = result["downloadfailed"] != null;
            if (downloadFailed) Thread.Sleep((int)(1000 * delay));
        }
        while (downloadFailed && attemptNb < attemptLimitNb);
        return result;
    }

public Dictionary<string, object> Download(string srcDirectoryPath, string file, string destDirectoryPath)
    {
        Exception downloadFailed = null;
        Dictionary<string, object> result = new Dictionary<string, object>();
        bool fileFound = false;

        try
        {
            if (destDirectoryPath == null || !Directory.Exists(destDirectoryPath)) throw new Exception("Download destination path does not exist");
            if (file != null && file != "")
            {
                if (file.Contains("/"))
                {
                    throw new Exception("Invalid file name. Impossible to download");
                }

                Uri serverUri;
                if (srcDirectoryPath == null || srcDirectoryPath == "")
                {
                    serverUri = new Uri("ftp://" + this.Server + "/" + file);
                }
                else if (Regex.IsMatch(srcDirectoryPath, "^/.*$") || Regex.IsMatch(srcDirectoryPath, "^.*/$"))
                {
                    throw new Exception("Path must not start and end with '/'");
                }
                else
                {
                    serverUri = new Uri("ftp://" + this.Server + "/" + srcDirectoryPath + "/" + file);
                }

                if (serverUri.Scheme != Uri.UriSchemeFtp) throw new Exception("server URI Scheme does not match  FTP URI Scheme");

                if (Exists(srcDirectoryPath, file))
                {
                    fileFound = true;

                    FtpWebRequest downloadRequest = (FtpWebRequest)FtpWebRequest.Create(serverUri);
                    downloadRequest.Credentials = new NetworkCredential(UserName, Password);
                    downloadRequest.KeepAlive = false;
                    downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
                    FtpWebResponse response = (FtpWebResponse)downloadRequest.GetResponse();

                    Stream responseStream = response.GetResponseStream();
                    FileStream fileStream = new FileStream(Path.Combine(destDirectoryPath, file), FileMode.Create);
                    byte[] buffer = new byte[2000];
                    int read = 0;
                    try
                    {
                        do
                        {
                            read = responseStream.Read(buffer, 0, buffer.Length);
                            fileStream.Write(buffer, 0, read);
                            fileStream.Flush();
                        }
                        while (read != 0);
                    }
                    catch (Exception e)
                    {
                        fileStream.Close();
                        responseStream.Close();
                        response.Close();
                        throw e;
                    }
                    fileStream.Close();
                    responseStream.Close();
                    response.Close();
                }
            }
        }
        catch (WebException webExcptn)
        {
            downloadFailed = webExcptn;
        }
        finally
        {
            result.Add("filefound", fileFound);
            result.Add("downloadfailed", downloadFailed);
        }

        return result;
    }

the Exists method :

public bool Exists(string srcPath, string elementName)
    {
        if (elementName == null || elementName == "")
        {
            return false;
        }

        Uri serverUri;
        bool res = false;

        if (srcPath == null || srcPath == "")
        {
            serverUri = new Uri("ftp://" + this.Server);
        }
        else if (Regex.IsMatch(srcPath, "^/.*$") || Regex.IsMatch(srcPath, "^.*/$"))
        {
            throw new Exception("Path must not start and end with '/'");
        }
        else
        {
            serverUri = new Uri("ftp://" + this.Server + "/" + srcPath);

        }
        if (serverUri.Scheme != Uri.UriSchemeFtp) throw new Exception("server URI Scheme does not match  FTP URI Scheme");

        FtpWebRequest listingRequest = (FtpWebRequest)FtpWebRequest.Create(serverUri);
        listingRequest.Credentials = new NetworkCredential(UserName, Password);
        listingRequest.KeepAlive = false;
        listingRequest.Method = WebRequestMethods.Ftp.ListDirectory;
        FtpWebResponse response = (FtpWebResponse)listingRequest.GetResponse();

        Stream responseStream = response.GetResponseStream();
        StreamReader streamReader = new StreamReader(responseStream);
        string ftpElementName;
        do
        {
            ftpElementName = Path.GetFileName(streamReader.ReadLine());
            if (ftpElementName == null) break;
            else
            {
                string pattern = "^" + elementName.Replace("[", "\\[").Replace("]", "\\]").Replace("+", "[+]").Replace(".", "[.]") + "$";
                if (Regex.IsMatch(ftpElementName, pattern, RegexOptions.IgnoreCase))
                {
                    res = true;
                }
            }
        }
        while (ftpElementName != null && !res);
        streamReader.Close();
        responseStream.Close();
        response.Close();

        return res;
    }

Maybe it's a timeout problem, but i don't really know. I searched for a long time for an answer but without success. Maybe some of you will have a solution.

///

EDIT : Some progress :

I have tested my code in debug mode with VS and in fact the Exception above is the consequence of a previous one. (I Couldn't know that because I only wrote the last Exception returned in a log file)

Here is the original exception :

Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

The second exception is caused by this part of the Download method code :

catch (Exception e)
{
    fileStream.Close();
    responseStream.Close();  // <<<<<<<<<<<<<<
    response.Close();
    throw e;
}

I keep on my investigations but it seems that the "timeout pb" hypothesis is the most consistent. I will try with a large timeout value tonight.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Hariboox
  • 171
  • 1
  • 1
  • 4
  • 1
    Don't apologise. The only French I remember is "Je ne parle Francais" and "Je voudrais une kilo du pomme du terre, s'il vous plais", neither of which is very useful, and probably horribly spelt :-) You've done a better job than I could in a similar situation. – paxdiablo Jun 10 '11 at 09:51
  • 14
    Your English is pretty good actually. You've managed to ask a question that is better worded and better structured than a lot of people who visit this site and speak English as their first language. – mdm Jun 10 '11 at 09:52
  • Also, what happens if you connect with a regular FTP client to the site? Can you download the file? It sounds as if there is a connectivity error between you and the server - your code looks OK. What about if you point your code at a different FTP site (maybe a local FTP server on your machine)? Does it fail then? – mdm Jun 10 '11 at 09:55
  • 1
    Perhaps the server is closing the connection but, only sometimes, perhaps your network is unreliable – Jodrell Jun 10 '11 at 09:58
  • @paxdiablo The only French I remember from school is "une biere s'il vous plait" and "ou est le toilette". Both invaluable members of my multi-lingual toolbox. What is more worrying is that I can barely remember any of this, but ZX-Spectrum BASIC and intimate details of the Apple IIcx are permanently engrained in some part of my brain. – mdm Jun 10 '11 at 09:58
  • 1
    "biere" and "toilette" are instantly recognisable to all who have experience with both ends of that particular process :-) – paxdiablo Jun 10 '11 at 10:00
  • @mdm : it's hard to test locally, because all files are located on a distant server and if i want to test on my own computer i have to download files first, but downloading 10 Go or 20 Go takes so long time ... and i'm not sure if the file used will reproduce the exception. :(. By the way, i noticed that Exceptions occur only on very large files (> 5 Go), but not everytime. – Hariboox Jun 10 '11 at 10:06
  • @Hariboox: It sounds like it is a time-out as suggested in the link ScottE posted. It might be worth just firing up your FTP client and trying to download the file though - that would at least tell you whether it is a problem with your code or a problem with the server and halve the amount of investigation you will have to do. – mdm Jun 10 '11 at 10:11
  • @mdm: Ok, i'm going to try to download with FileZilla but i'm pretty sure that there is no problem with the server. but let's wait and see :) – Hariboox Jun 10 '11 at 10:17
  • @Hariboox, you can also create your _own_ 5G file for local testing instead of downloading. It's unlikely to be the _content_ that's causing problems, but I won't rule it out altogether :-) – paxdiablo Jun 10 '11 at 10:57
  • @paxdiablo: yes for sure ^^. But I want to test it on the original files, because i'm not completely sure that it's not their content which cause these Exceptions ... Well, it would be weird but ... – Hariboox Jun 10 '11 at 11:07
  • have you looked at transient error handling? I would perhaps add a retry policy this would typically resolve SQL Connection issues – Christo Oct 29 '12 at 03:54

4 Answers4

15

Just want to strengthen ScottE's diagnosis, and be more specific. Timeout is most likely the issue.

Either .Net implementation of FtpWebRequest is erroneous or the MSDN document has a typo, the default value of FtpWebRequest.Timeout is not -1 (Infinite). It is 100000 (100 seconds).

In addition there is another timeout issue. A few tests have shown that responseStream always has a timeout value of 300000 (300 seconds). I do not know how this value is assigned. Anyways, this value needs to be modified to accommodate large files.

In summary, the solution is to set FtpWebRequest.Timeout and Stream.Timeout to a sufficiently large value.

Hong
  • 17,643
  • 21
  • 81
  • 142
  • 1
    I can confirm that the FtpWebRequest.Timeout documentation is wrong. @Hong is right... it's 100 seconds. – Mike L Feb 12 '13 at 05:30
2

Here's a good thread of things to try:

http://social.msdn.microsoft.com/Forums/en/ncl/thread/47634ec2-4d40-4d3f-b075-8cc92bfa2b24

Increasing the timeout is probably a good idea in the least.

ScottE
  • 21,530
  • 18
  • 94
  • 131
  • i'm going to try this solution. – Hariboox Jun 10 '11 at 10:17
  • I don't know that anything solved that particular problem, but it looks similar to what you're experiencing. It can get complicated with large files. – ScottE Jun 10 '11 at 10:22
  • @Hariboox - I encourage you to use the 'using' construct to automatically call the dispose methods on any method that implements IDisposable. It helps prevent leaks or locks. – ScottE Jun 10 '11 at 19:28
0

This could be a symptom of an issue with the settings of your Windows Firewall. Disabling the "Application Layer Gateway Service" in the "services" interface fixed it for me.

This thread has a lot of information:

http://forum.parallels.com/pda/index.php/t-57966.html

JerSchneid
  • 5,817
  • 4
  • 34
  • 37
0

Try setting:

request.EnableSsl = true
Kenny Horna
  • 13,485
  • 4
  • 44
  • 71