底层连接关闭:接收时发生了意外错误

17

我在这里因为在使用FTP协议下载某些文件时遇到了问题。很奇怪,因为它偶尔发生,即使是对于同一个文件。

只是要说明一下:我正在下载非常大的文件(从500Mo到30Go)

以下是由我的函数返回的异常类型:(抱歉,是用法语写的)

System.Net.WebException: 连接已关闭:在接收时发生了意外错误。 at System.Net.FtpWebRequest.CheckError() at System.Net.FtpWebRequest.SyncRequestCallback(Object obj) at System.IO.Stream.Close() at System.Net.ConnectionPool.Destroy(PooledStream pooledStream) at System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse) at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage) at System.Net.FtpWebRequest.SyncRequestCallback(Object obj) at System.Net.CommandStream.Abort(Exception e) at System.Net.CommandStream.CheckContinuePipeline() at System.Net.FtpDataStream.System.Net.ICloseEx.CloseEx(CloseExState closeState) at System.Net.FtpDataStream.Dispose(Boolean disposing) at System.IO.Stream.Close() at UtilityClasses.FTP.Download(String srcDirectoryPath, String file, String destDirectoryPath)

以下是用于下载的代码:

下载方法:

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;
    }

也许这是一个超时问题,但我真的不确定。我搜索了很久,但没有找到答案。也许你们中的一些人会有解决方案。
///
编辑:有一些进展:
我已经在VS的调试模式下测试了我的代码,实际上上面的异常是先前异常的结果。(我不知道这一点,因为我只把最后一个返回的异常写入了日志文件)
这是原始的异常:
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.

第二个异常是由Download方法代码中的这部分引起的:
catch (Exception e)
{
    fileStream.Close();
    responseStream.Close();  // <<<<<<<<<<<<<<
    response.Close();
    throw e;
}

我继续进行调查,但似乎“超时问题”假设是最一致的。今晚我会尝试使用更长的超时时间。


1
不要道歉。我唯一记得的法语是“Je ne parle Francais”和“Je voudrais une kilo du pomme du terre, s'il vous plais”,两者都不是很有用,而且可能拼写错误 :-) 在类似的情况下,你做得比我好。 - paxdiablo
14
你的英语其实很不错。你成功地提出了一个措辞更好、结构更好的问题,比许多以英语为母语的人访问此网站的语言表达都要好。 - mdm
1
也许是服务器关闭了连接,但有时候也可能是你的网络不稳定。 - Jodrell
1
“biere”和“toilette”对于那些有过这两个特定经历的人来说是非常容易识别的 :-) - paxdiablo
你有考虑过瞬态错误处理吗?我建议加入重试策略,这通常可以解决SQL连接问题。 - Christo
显示剩余7条评论
4个回答

15

我只是想加强ScottE的诊断,并更具体地指出问题所在。超时很可能是问题所在。

无论是 .Net 实现的 FtpWebRequest 有误还是 MSDN 文档存在错字,FtpWebRequest.Timeout 的默认值不是 -1(无限)。它是 100000(100 秒)。

此外,还存在另一个超时问题。几次测试表明,responseStream 始终具有 300000(300 秒)的超时值。我不知道如何分配此值。无论如何,需要修改此值以容纳大文件。

总之,解决方案是将 FtpWebRequest.Timeout 和 Stream.Timeout 设置为足够大的值。


1
我可以确认FtpWebRequest.Timeout文档是错误的。@Hong是正确的...它是100秒。 - Mike L

2

我将尝试这个解决方案。 - Hariboox
我不知道是否有任何解决那个特定问题的方法,但它看起来与您所遇到的情况相似。在处理大文件时可能会变得复杂。 - ScottE
@Hariboox - 我鼓励你使用“using”结构,在任何实现IDisposable的方法上自动调用dispose方法。这有助于防止泄漏或锁定。 - ScottE

0

0

尝试设置:

request.EnableSsl = true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接