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

EDIT : 理解を深めるために、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;
    }
}

EDIT 2: ところで...接続が失敗したときにのみ、この未処理の例外が発生します。FTP ソースが利用可能であれば、すべて正常に動作します。

4

1 に答える 1

4

簡単な答え: これは Mono のバグです (実際には、FtpWebRequest のいくつかのバグの 1 つです)。可能であれば、別の 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() は、仕様上は同期ですが、実際には内部では非同期であることがわかります [sic!] -- 422 行目です。これにより、エラーが発生しやすい動的環境が作成されます。

失敗したシナリオの 1 つ (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 に答える