WebClient.DownloadFile と正しい URL で操作がタイムアウトしました
質問
製品をデータベースにバッチアップロードしています。
商品に使用する画像のURLをサイトにダウンロードします。
私が書いたコードは、最初の 25 回の反復 (何らかの理由で常にその回数) までは正常に動作しますが、その後、System.Net.WebException「操作がタイムアウトしました」がスローされます。
if (!File.Exists(localFilename))
{
using (WebClient Client = new WebClient())
{
Client.DownloadFile(remoteFilename, localFilename);
}
}
要求していたリモート URL を確認したところ、画像を返す有効な画像 URL でした。
また、デバッガーでステップ実行しても、タイムアウト エラーは発生しません。
ヘルプ!;)
解決
私があなたの立場だったら、次のような可能性を調査します。
- このコードを複数のスレッドから実行している場合は、System.Net.ServicePointManager.DefaultConnectionLimit プロパティと衝突する可能性があります。アプリを起動するときに、この値を 50 ~ 100 に増やしてみてください。これがあなたの問題ではないと思いますが、これを試すのは以下の他の方法よりも簡単です。:-)
- もう 1 つの可能性は、サーバーに負荷がかかっていることです。通常、これをシングルスレッドのクライアントで実行するのは困難ですが、他の複数のクライアントもサーバーにアクセスする可能性があるため、これは可能です。しかし、問題は常に #25 で発生するため、さらに多くのバリエーションが見られることが予想されるため、これは起こりそうにありません。
- クライアントとサーバー間のキープアライブ HTTP 接続のバックアップで問題が発生している可能性があります。これも可能性は低いと思われます。
- 25 というハード カットオフは、これがユーザー側またはサーバー側のプロキシまたはファイアウォールの制限である可能性があり、1 つのクライアント IP から 1 つのサーバー (またはプロキシ) に対して 25 を超える接続が制限されるのではないかと考えます。
私の意見は後者の方にあります。なぜなら、この関数はかなりの数のリクエストで常に中断するという事実と、デバッガを実行しても (つまり、遅い!) 問題が引き起こされないからです。
これらすべてをテストするには、次の簡単なことから始めます。各 HTTP 呼び出しの前に遅延 (Thread.Sleep) を設定し、問題が解消されるかどうかを確認します。発生した場合は、問題が再発するまでの遅延を減らします。そうでない場合は、遅延を大きな数値まで増やします (例:問題が解決するまで 10 秒)。10 秒待っても治らない場合、それは本当に謎であり、診断するにはさらに詳しい情報が必要です。
遅れて解消される場合は、その理由と、制限が永続的なものであるかどうかを理解する必要があります (例:サーバーのファイアウォールは変更できません)、または変更できるもの。さらに詳しい情報を取得するには、リクエストの時間を計測する必要があります (例:各呼び出しの前後に DateTime.Now をチェックして、パターンが見られるかどうかを確認します。タイミングがすべて一貫していて、突然大きくなる場合は、ネットワーク/ファイアウォール/プロキシのスロットルが発生していることを示唆しています。タイミングが徐々に増加する場合は、サーバーが徐々に過負荷になり、リクエスト キューが長くなっていることを示唆しています。
リクエストのタイミングに加えて、Web クライアント呼び出しのタイムアウトを長めに設定します。これにより、タイムアウトが無限であるか、デフォルトより少しだけ長いかを判断できます。これを行うには、WebClient クラスの代替クラスが必要になります。WebClient クラスはタイムアウトをサポートしていないためです。 このスレッド MSDN フォーラムには、合理的な代替コード サンプルがあります。
コードにタイミングを追加する代わりに、Fiddler を使用することもできます。
- ダウンロード バイオリン弾き そしてそれを起動します。
- Web クライアント コードの Proxy プロパティを Fiddler プロキシ (localhost:8888) を指すように設定します。
- アプリを実行して fiddler を確認します。
他のヒント
それはWebClient
が行われたときに、それはあなたのケースでは、多くの回答が同時に、リモートサーバー上の25台の接続の制限を開くことが、原因となる使用Response
オブジェクトをクローズされていないようです、あなたは "ですタイムアウト例外」。あなたがデバッグする場合、早期reponsesは、それらの内側のタイムアウトなどに閉じます開きました...
(私はリフレクターで、私は応答を閉じるための指示を見つけることができないことをWebClient
inpected)。
私はあなたがそれぞれのダウンロード後のオブジェクトをきれいにすることができるように、あなたがHttpWebRequest & HttpWebResponse
を使用することをpropseます:
HttpWebRequest request;
HttpWebResponse response = null;
try
{
FileStream fs;
Stream s;
byte[] read;
int count;
read = new byte[256];
request = (HttpWebRequest)WebRequest.Create(remoteFilename);
request.Timeout = 30000;
request.AllowWriteStreamBuffering = false;
response = (HttpWebResponse)request.GetResponse();
s = response.GetResponseStream();
fs = new FileStream(localFilename, FileMode.Create);
while((count = s.Read(read, 0, read.Length))> 0)
{
fs.Write(read, 0, count);
count = s.Read(read, 0, read.Length);
}
fs.Close();
s.Close();
}
catch (System.Net.WebException)
{
//....
}finally
{
//Close Response
if (response != null)
response.Close();
}
ここで卍の答えを少し簡略化されたバージョンです
private static void DownloadFile(Uri remoteUri, string localPath)
{
var request = (HttpWebRequest)WebRequest.Create(remoteUri);
request.Timeout = 30000;
request.AllowWriteStreamBuffering = false;
using (var response = (HttpWebResponse)request.GetResponse())
using (var s = response.GetResponseStream())
using (var fs = new FileStream(localPath, FileMode.Create))
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = s.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, bytesRead);
bytesRead = s.Read(buffer, 0, buffer.Length);
}
}
}
私は同じ問題を抱えていると私はそれがコンフィギュレーションファイルはapp.configにこの行を追加解くます:
<system.net>
<connectionManagement>
<add address="*" maxconnection="100" />
</connectionManagement>
</system.net>