Mono for Android / MonoTouch System.Net.WebException:请求已中止

时间:2013-04-17 11:34:45

标签: c# exception mono xamarin.ios xamarin.android

当我使用以下代码来请求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分钟后,我得到了上面未处理的例外。

AsynTask:

#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

SimpleTest:

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源工作正常。

1 个答案:

答案 0 :(得分:4)

简短回答:这是一个Mono bug(实际上是FtpWebRequest中的一个)。 切换到备用FTP客户端类/ libary(例如J.P. 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()虽然是每个规范同步,但实际上是异步的[sic!] - 第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*

Aaaand就是这样的。不幸的是,修复属于Mono。

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