有连接错误时的异步FTP

时间:2015-04-10 14:03:30

标签: c# .net asynchronous .net-4.0 ftpwebrequest

我制作了一个Windows服务,上传了放入文件夹的所有文件(图片)。

ftp工作异步,基于msdn代码。

一切正常,直到我拔下网线。

ftp方法应该捕获错误并返回false bool。然后我将文件移动到一个失败的文件夹,然后继续下一个文件。 到达第3个文件时,WaitOne操作永远不会停止,并且永远不会到达EndGetStreamCallback。我真的不知道发生了什么。 在此之后,服务也会变得无法响应,直到我重新启动它。

文件处理程序:

string[] files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
            AsynchronousFtpUpLoader ftp = new AsynchronousFtpUpLoader();
            foreach (string file in files) {
                var result = ftp.Upload(ConfigurationManager.AppSettings["ftp_host"] + ConfigurationManager.AppSettings["ftp_path"] + file.Replace(path, "").Substring(1), file);
                if (!result) { 
                    return false;
                }
            }

AsynchronousFtpUpLoader:

public class AsynchronousFtpUpLoader
    {
        public bool Upload(string sTarget,string file)
        {
            bool succeed = false;
            // Create a Uri instance with the specified URI string. 
            // If the URI is not correctly formed, the Uri constructor 
            // will throw an exception.
            ManualResetEvent waitObject;

            Uri target = new Uri(sTarget);
            string fileName = file;
            FtpState state = new FtpState();

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(target);
            request.Method = WebRequestMethods.Ftp.UploadFile;

            request.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["ftp_user"], ConfigurationManager.AppSettings["ftp_password"]);

            // Store the request in the object that we pass into the 
            // asynchronous operations.
            state.Request = request;
            state.FileName = fileName;

            // Get the event to wait on.
            waitObject = state.OperationComplete;

            // Asynchronously get the stream for the file contents.
            request.BeginGetRequestStream(
                new AsyncCallback(EndGetStreamCallback),
                state
            );

            // Block the current thread until all operations are complete.
            waitObject.WaitOne();

            // The operations either completed or threw an exception. 
            if (state.OperationException != null)
            {
                Log.writeLog("ERROR","FTP error", sTarget.Substring(0, sTarget.IndexOf("_")).Substring(sTarget.LastIndexOf("\\") + 1), sTarget);
                Log.writeError(state.OperationException.ToString() + (state.OperationException.InnerException != null ? state.OperationException.InnerException.ToString() : ""), sTarget);
                //throw state.OperationException;
            }
            else
            {
                succeed = true;
                Console.WriteLine("The operation completed - {0}", state.StatusDescription);
            }

            return succeed;
        }
        private static void EndGetStreamCallback(IAsyncResult ar)
        {
            FtpState state = (FtpState)ar.AsyncState;

            Stream requestStream = null;
            // End the asynchronous call to get the request stream. 
            try
            {
                Console.WriteLine("Opened the stream");
                using(requestStream = state.Request.EndGetRequestStream(ar)){
                    // Copy the file contents to the request stream. 
                    const int bufferLength = 2048;
                    byte[] buffer = new byte[bufferLength];
                    int count = 0;
                    int readBytes = 0;
                    using (FileStream stream = File.OpenRead(state.FileName))
                    {
                        do
                        {
                            readBytes = stream.Read(buffer, 0, bufferLength);
                            requestStream.Write(buffer, 0, readBytes);
                            count += readBytes;
                        }
                        while (readBytes != 0);
                        Console.WriteLine("Writing {0} bytes to the stream.", count);
                        // IMPORTANT: Close the request stream before sending the request.
                        requestStream.Close();
                        stream.Close();
                    }
                }
                Console.WriteLine("Closed the stream");
                // Asynchronously get the response to the upload request.
                state.Request.BeginGetResponse(
                    new AsyncCallback(EndGetResponseCallback),
                    state
                );
            }
            // Return exceptions to the main application thread. 
            catch (Exception e)
            {
                Console.WriteLine("Could not get the request stream.");
                state.OperationException = e;
                state.OperationComplete.Set();

                if (requestStream != null) {
                    requestStream.Close();
                }
                return;
            }

        }

        // The EndGetResponseCallback method   
        // completes a call to BeginGetResponse. 
        private static void EndGetResponseCallback(IAsyncResult ar)
        {
            FtpState state = (FtpState)ar.AsyncState;
            FtpWebResponse response = null;
            try
            {
                response = (FtpWebResponse)state.Request.EndGetResponse(ar);
                response.Close();
                state.StatusDescription = response.StatusDescription;
                // Signal the main application thread that  
                // the operation is complete.
                state.OperationComplete.Set();
            }
            // Return exceptions to the main application thread. 
            catch (Exception e)
            {
                Console.WriteLine("Error getting response.");
                state.OperationException = e;
                state.OperationComplete.Set();

                if (response != null) {
                    response.Close();
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:0)

有同样的问题。代码基于相同的MSDN示例,但已增强,可通过SSL / TLS工作。 文件可以成功上传数小时或数天,但有时会挂在互联网连接不良的环境中 - 主线程永远被阻止。 使用http://jagt.github.io/clumsy/重现问题并对其进行调试。看来,当上传字节完成时,我们坚持在这里关闭请求流

                        // IMPORTANT: Close the request stream before sending the request.
                    requestStream.Close();

当我们调用它时,FtpWebRequest关闭上传连接并等待操作完成。就FtpWebRequest而言,stage必须更改为ReleaseConnection。当FTP服务器通过控制通道发送消息,上传完成时(当我们关闭二进制连接时),完成此阶段更改。出于某种原因,它从未发生过。可能是由于网络错误ftp客户端没有从服务器接收消息并且永远等待。

可能的解决方案:

  1. 实现在requestStream.Close()
  2. 上超时后中止连接的监视程序
  3. 似乎与KeepAlive = false它可能更稳定(未完全测试)