有连接错误时的异步FTP

时间:2022-06-01 20:37:22

I made a windows service, that uploads all the files (pictures) that are put into a folder.

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

The ftp works async, and was based on the msdn code.

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

All works fine, until I unplug the network cable.

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

The ftp method is supposed to catch the error and return a false bool. Then I move the file to a failed folder, and it continues with the next file. When the 3rd file is reached, the WaitOne action never stops, and the EndGetStreamCallback is never reached. I really don't know what is going on. After this the service also becomes unresponsive until I restart it.

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

The file handler:

文件处理程序:

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

The AsynchronousFtpUpLoader :

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 个解决方案

#1


0  

Had the same problem. Code based on the same MSDN example, but enhanced to work over SSL/TLS. Files can successfully uploads hours or day but sometimes hangs in environment with bad internet connection - main thread blocked forever. Reproduced the problem using http://jagt.github.io/clumsy/ and debugged it. It seems that when uploading bytes finished we stucked on closing requeststream here

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

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

When we call this, FtpWebRequest closed the uploading connection and waits operation to be done. In terms of FtpWebRequest, stage must be changed to ReleaseConnection. This stage change is done when FTP server send message over control channel, that uploading is done (when we closed binary connection). For some reason it never happens. May be due network errors ftp client doesn't receive message from server and waits forever.

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

Possible solutions:

  1. Implement watchdog that abort connection after timeout on requestStream.Close()
  2. 实现在requestStream.Close()超时后中止连接的监视程序

  3. Seems like with KeepAlive = false it may works more stable (not fully tested)
  4. 似乎与KeepAlive = false它可能更稳定(未完全测试)

#1


0  

Had the same problem. Code based on the same MSDN example, but enhanced to work over SSL/TLS. Files can successfully uploads hours or day but sometimes hangs in environment with bad internet connection - main thread blocked forever. Reproduced the problem using http://jagt.github.io/clumsy/ and debugged it. It seems that when uploading bytes finished we stucked on closing requeststream here

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

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

When we call this, FtpWebRequest closed the uploading connection and waits operation to be done. In terms of FtpWebRequest, stage must be changed to ReleaseConnection. This stage change is done when FTP server send message over control channel, that uploading is done (when we closed binary connection). For some reason it never happens. May be due network errors ftp client doesn't receive message from server and waits forever.

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

Possible solutions:

  1. Implement watchdog that abort connection after timeout on requestStream.Close()
  2. 实现在requestStream.Close()超时后中止连接的监视程序

  3. Seems like with KeepAlive = false it may works more stable (not fully tested)
  4. 似乎与KeepAlive = false它可能更稳定(未完全测试)