Windows Phone の WebRequest のキャンセルとタイムアウト
Windows Phone の WebRequest にはタイムアウトの設定がありません。
タイムアウト機構は呼び出し側が独自に実装する必要があります。
今回の例はWebRequestの非同期リクエストとWaitHandleを組み合わせることでタイムアウトとキャンセルを提供します。
複数の条件に対する待機を WaitHandle.WaitAny で束ねることで、待機完了後の判断に曖昧さがなくなるのがポイントです。
// 上流の別スレッドから、処理のキャンセル要求があった場合にセットされる var cancel_wait_handle = new ManualResetEvent( false ); // 非同期リクエストが完了したらセットされる var response_wait_handle = new ManualResetEvent( false ); var list_wait_handle = new WaitHandle[] { cancel_wait_handle,response_wait_handle }; // リクエストを送る response_wait_handle.Reset(); var async_result = req.BeginGetResponse( (ar)=>{ response_wait_handle.Set(); },null ); switch( WaitHandle.WaitAny( list_wait_handle,timeout_ms) ){ case WaitHandle.WaitTimeout: // タイムアウトした req.Abort(); result.last_status = 0; result.last_error = "Connect Timeout"; log.e( "Connect Timeout. url={0}",url ); continue; case 0: // キャンセルされた req.Abort(); log.e( "HTTP request was cancelled."); return null; } (中略) // レスポンスのコンテンツを読む var src = res.GetResponseStream(); if( src != null ){ try{ var content_length_reported = (int)res.ContentLength; if( content_length_reported <=4096 ) content_length_reported = 4096; using( var dst = new MemoryStream( content_length_reported ) ) { Boolean bTimeout = false; for( ; ; ) { response_wait_handle.Reset(); async_result = src.BeginRead( tmp,0,tmp.Length,( ar ) => { response_wait_handle.Set(); },null ); switch( WaitHandle.WaitAny( list_wait_handle,timeout_ms ) ) { case WaitHandle.WaitTimeout: // タイムアウトした result.last_status = 0; result.last_error = "Read Timeout"; log.e( "Read Timeout. url={0}",url ); bTimeout = true; break; case 0: // キャンセルされた log.e( "HTTP request was cancelled." ); return null; } int delta = src.EndRead( async_result ); if( delta <= 0 ) break; dst.Write( tmp,0,delta ); } if( bTimeout ) continue; // レスポンスボディ読み取り成功 result.content_bytes = dst.ToArray(); } }catch(IOException ex){ result.last_status = 0; result.last_error= TextUtil.format_ex(ex); log.ex_short(ex,"Fail to read response body"); continue; }finally{ src.Close(); } }
なお、このコードは全体としてはブロッキングするようになっているので UIスレッドから実行してはいけません。