4

当我使用以下代码向 FTP 服务器发出请求时。如果请求失败,我会收到错误消息。我不知道为什么。

代码:

try
{
      FtpWebRequest request = GetFtpWebResquest(WebRequestMethods.Ftp.ListDirectoryDetails, shareInfo.Uri);
      FtpWebResponse response = (FtpWebResponse)request.GetResponse();
      root = new FtpResource(this, response);
}
catch (Exception e)
{                  
}

例外:

Unhandled Exception:
System.Net.WebException: Request aborted
at System.Net.FtpWebRequest.CheckIfAborted () [0x00000] in <filename unknown>:0 
at System.Net.FtpWebRequest.set_State (RequestState value) [0x00000] in <filename     unknown>:0 
at System.Net.FtpWebRequest.ProcessRequest () [0x00000] in <filename unknown>:0 
at System.Threading.Thread.StartInternal () [0x00000] in <filename unknown>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.Net.WebException: Request aborted
at System.Net.FtpWebRequest.CheckIfAborted () [0x00000] in <filename unknown>:0 
at System.Net.FtpWebRequest.set_State (RequestState value) [0x00000] in <filename unknown>:0 
at System.Net.FtpWebRequest.ProcessRequest () [0x00000] in <filename unknown>:0 
at System.Threading.Thread.StartInternal () [0x00000] in <filename unknown>:0 
@@@ ABORTING: INVALID HEAP ADDRESS IN dlfree addr=0x420e3580
_wapi_handle_ref: Attempting to ref unused handle 0x41c
* Assertion at /Users/builder/data/lanes/monodroid-lion-bs1/03814ac5/source/mono/mono/io-layer/wthreads.c:1365, condition `ok' not met

编辑: 为了更好地理解,我实现了一个从 AsyncTask 调用的简单测试方法。AsnycTask 启动一次,除了 GUI 线程和此 AsyncTask 之外,我不使用其他线程。每隔几秒钟,AsyncTask 就会收到我的 ListView 的所有条目并设置状态(离线/在线)。2 - 3 分钟后,我得到了上面未处理的异常。

异步任务:

#region implemented abstract members of AsyncTask
protected override Java.Lang.Object DoInBackground (params Java.Lang.Object[] @params)
{
try
{
    _isRunning = true;
    while (_isRunning) 
    {                     
        if (Statics.IsInternetAvailable(_context))
        {
            AvailableShareAdapter adapter = _context.GetAvailableSharesAdapter();
            if (adapter != null)
            {
                List<ShareInformation> list = adapter.GetAllItems();
                foreach(ShareInformation si in list)
                {
                    if (!_isRunning)
                        break;

                    si.State = ShareInformation.ShareState.Testing;
                    _handler.Post(new Java.Lang.Runnable(() => { _context.GetAvailableSharesAdapter().NotifyDataSetChanged(); } ));

                    if (_coreFacade.SimpleTest(si.Uri, si.UserName, si.Password))
                         si.State = ShareInformation.ShareState.Online;
                    else
                         si.State = ShareInformation.ShareState.Offline;

                   _handler.Post(new Java.Lang.Runnable(() => { _context.GetAvailableSharesAdapter().NotifyDataSetChanged(); } ));

              }
         }
         Thread.Sleep(60000); 
      }                 
    }
} catch(Exception e)
{
    _coreFacade.Log("DoInBackground", e.ToString());
}

return null;
}
#endregion

简单测试:

public bool SimpleTest(string uri, string username, string password)
{
    Console.WriteLine("SimpleTest called");
    try {
        WebRequest request = HttpWebRequest.Create(uri);
        request.Credentials = new NetworkCredential(username, password);
        request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
        request.Timeout = 30 * 1000;
        FtpWebResponse response = (FtpWebResponse)request.GetResponse();
        return true;
    } catch (Exception ex) {
        Console.WriteLine(ex.ToString());
        return false;
    }
}

编辑2: 顺便说一句......我只在连接失败时得到这个未处理的异常。如果 FTP 源可用,一切正常。

4

1 回答 1

4

简短的回答:这是一个 Mono 错误(实际上是 FtpWebRequest 中的几个错误之一)。如果可能,切换到替代 FTP 客户端类/库(例如 JP Trosclair 的 System.Net.FtpClient)。

长答案:

抛出“请求中止”异常的线程是运行时创建的线程,这就是您无法捕获异常的原因(以及运行时终止的原因)。

我最近遇到了同样的问题,花了很多时间调试它。

如果您查看 Mono 的 FtpWebRequest(截至目前的最新来源):

https://github.com/mono/mono/blob/5aeeb9cf7b558b8a92a7eb7d6a524cc4e7b60191/mcs/class/System/System.Net/FtpWebRequest.cs

您将看到 ProcessRequest(来自您的堆栈跟踪)是一个 ThreadStart 委托,并且感兴趣的线程确实是由运行时创建的(第 391 和 442 行)。

事实证明,FtpWebReuqest.GetResponse() 虽然按照规范是同步的,但实际上在引擎盖下是异步的 [原文如此!] —— 第 422 行。这创建了动态环境(我们很快就会看到)容易出错。

以下是在失败场景之一(TCP connect() 超时)中发生的情况:

[calling thread] Create(); // FtpWebRequest object, State == RequestState.Before
[calling thread] GetResponse();
[calling thread]  BeginGetResponse();
[calling thread]    State = RequestState.Scheduled;
[calling thread]    new Thread(ProcessRequest()).Start() // runtime-created thread
[calling thread]  EndGetResponse();
[calling thread]    WaitUntilComplete();
[runtime thread]  ProcessRequest();
[runtime thread]    ProcessMethod();
[runtime thread]      State = RequestState.Connecting;
[runtime thread]      OpenControlConnection();
[runtime thread]        Socket.Connect();
         <time passes>
[calling thread]    Abort(); // WaitUntilComplete() times out
[calling thread]    State = RequestState.Aborted; // [!!]
[calling thread]    throw WebException("Transfer timed out");
                    // calling thread seems to think everything's fine, but...
         <some time later>
[runtime thread]        throw SocketException // Socket.Connect() times out
[runtime thread]        sock = null; // from exception handler
[runtime thread]        throw WebException("unable to connect to remote server");
                         // because sock was set to null
[runtime thread]    State = RequestState.Error;
                    // exception is caught in ProcessRequest, an attempt is made to
                    // change the State to RequestState.Error, but it's already been
                    // set to RequestState.Aborted by the calling thread [!!]
[runtime thread]      throw WebException("Request aborted");
                    // and this exception isn't caught anywhere... *KABOOM*

啊,事情就是这样发生的。不幸的是,修复属于 Mono。

您当然可以尝试通过设置超过底层套接字的 connect() 超时的超时来解决它,但事实是,很少有其他代码路径可以让 Mono 抛出无法处理的异常...... :-(

于 2013-07-11T06:25:03.593 回答