.NET WebClientオブジェクトのタイムアウトを変更する方法


230

クライアントのデータをローカルマシンに(プログラムで)ダウンロードしようとしていますが、クライアントのWebサーバーが非常に遅いため、WebClientオブジェクトでタイムアウトが発生しています。

これが私のコードです:

WebClient webClient = new WebClient();

webClient.Encoding = Encoding.UTF8;
webClient.DownloadFile(downloadUrl, downloadFile);

このオブジェクトに無限のタイムアウトを設定する方法はありますか?それとも、これを行う別の方法の例を誰かが私に手伝ってもらえませんか?

URLはブラウザで正常に機能します-表示されるのに約3分かかります。

回答:


378

タイムアウトを拡張できます。次の例のように、元のWebClientクラスを継承し、webrequestゲッターをオーバーライドして独自のタイムアウトを設定します。

私の場合、MyWebClientはプライベートクラスでした。

private class MyWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest w = base.GetWebRequest(uri);
        w.Timeout = 20 * 60 * 1000;
        return w;
    }
}

5
デフォルトのタイムアウトは何ですか?
knocte

23
デフォルトのタイムアウトは100秒です。30秒間走っているようですが。
Carter Medlin

3
タイムスパンTimeSpan.FromSeconds(20).Millisecondsでタイムアウトを設定する方が少し簡単です...
webwires 2014年

18
@webwires使用すべきであり、使用すべきでは.TotalMillisecondsありません.Milliseconds
Alexander Galkin 14

80
クラスの名前は次のとおりです。PatientWebClient;)
Jan Willem B

27

最初の解決策は私にとってはうまくいきませんでしたが、私のためにうまくいったいくつかのコードがあります。

    private class WebClient : System.Net.WebClient
    {
        public int Timeout { get; set; }

        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest lWebRequest = base.GetWebRequest(uri);
            lWebRequest.Timeout = Timeout;
            ((HttpWebRequest)lWebRequest).ReadWriteTimeout = Timeout;
            return lWebRequest;
        }
    }

    private string GetRequest(string aURL)
    {
        using (var lWebClient = new WebClient())
        {
            lWebClient.Timeout = 600 * 60 * 1000;
            return lWebClient.DownloadString(aURL);
        }
    }

21

あなたは使用する必要があるHttpWebRequestのではなくWebClient、あなたが上のタイムアウトを設定することはできませんとWebClient(それが使用するにもかかわらず、それを拡張せずHttpWebRequest)。使用してHttpWebRequest、あなたがタイムアウトを設定することができます代わりに。


これは真実ではありません... WebRequestをオーバーライドしてタイムアウトを設定するカスタム実装ではありますが、WebClientを引き続き使用できることが上記でわかります。
DomenicDatti 2014

7
「System.Net.HttpWebRequest.HttpWebRequest()」は廃止されました:「このAPIは.NET Frameworkインフラストラクチャをサポートしており、コードから直接使用するためのものではありません」
有用なビー

3
@usefulBee-そのコンストラクターを呼び出すべきではないため:msdn.microsoft.com/en-us/library/…"Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."から。また、stackoverflow.com
questions / 400565 /…

明確にするために:この特定のコンストラクタは回避する必要がありますが(とにかく新しい.NETバージョンの一部ではなくなります)、のTimeoutプロパティを使用することは完全に問題ありませんHttpWebRequest。ミリ秒単位です。
Marcel、

10

ネットワークケーブルを抜いたときにw.Timeoutコードを機能させることができませんでした。タイムアウトが発生せず、HttpWebRequestを使用するようになり、今すぐジョブを実行します。

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downloadUrl);
request.Timeout = 10000;
request.ReadWriteTimeout = 10000;
var wresp = (HttpWebResponse)request.GetResponse();

using (Stream file = File.OpenWrite(downloadFile))
{
    wresp.GetResponseStream().CopyTo(file);
}

1
この答えはうまくいきますが、興味のある人にとっては、var wresp = await request.GetResponseAsync();varの代わりに使用wresp = (HttpWebResponse)request.GetResponse();すると、大きなタイムアウトが再び発生します
andrewjboyd

andrewjboyd:GetResponseAsync()が機能しない理由を知っていますか?
osexpert

9

完全を期すために、VBに移植されたkispのソリューションを以下に示します(コメントにコードを追加できません)。

Namespace Utils

''' <summary>
''' Subclass of WebClient to provide access to the timeout property
''' </summary>
Public Class WebClient
    Inherits System.Net.WebClient

    Private _TimeoutMS As Integer = 0

    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal TimeoutMS As Integer)
        MyBase.New()
        _TimeoutMS = TimeoutMS
    End Sub
    ''' <summary>
    ''' Set the web call timeout in Milliseconds
    ''' </summary>
    ''' <value></value>
    Public WriteOnly Property setTimeout() As Integer
        Set(ByVal value As Integer)
            _TimeoutMS = value
        End Set
    End Property


    Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
        Dim w As System.Net.WebRequest = MyBase.GetWebRequest(address)
        If _TimeoutMS <> 0 Then
            w.Timeout = _TimeoutMS
        End If
        Return w
    End Function

End Class

End Namespace

7

ソニーが言うように、を使用する代わりにプロパティを使用System.Net.HttpWebRequestして設定しTimeoutますSystem.Net.WebClient

ただし、無限のタイムアウト値を設定することはできません(これはサポートされておらず、設定しようとするとがスローされますArgumentOutOfRangeException)。

最初にHEAD HTTPリクエストを実行し、Content-Length返されたヘッダー値を調べてダウンロードするファイルのバイト数を判断し、その後のGETリクエストに応じてタイムアウト値を設定するか、または非常に長いタイムアウト値を指定することをお勧めします超えることを期待することはありません。


7
'CORRECTED VERSION OF LAST FUNCTION IN VISUAL BASIC BY GLENNG

Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
            Dim w As System.Net.WebRequest = MyBase.GetWebRequest(address)
            If _TimeoutMS <> 0 Then
                w.Timeout = _TimeoutMS
            End If
            Return w  '<<< NOTICE: MyBase.GetWebRequest(address) DOES NOT WORK >>>
        End Function

5

非同期/タスクメソッド機能するタイムアウト付きのWebクライアントが必要場合は、推奨されるソリューションは機能しません。機能するものは次のとおりです。

public class WebClientWithTimeout : WebClient
{
    //10 secs default
    public int Timeout { get; set; } = 10000;

    //for sync requests
    protected override WebRequest GetWebRequest(Uri uri)
    {
        var w = base.GetWebRequest(uri);
        w.Timeout = Timeout; //10 seconds timeout
        return w;
    }

    //the above will not work for async requests :(
    //let's create a workaround by hiding the method
    //and creating our own version of DownloadStringTaskAsync
    public new async Task<string> DownloadStringTaskAsync(Uri address)
    {
        var t = base.DownloadStringTaskAsync(address);
        if(await Task.WhenAny(t, Task.Delay(Timeout)) != t) //time out!
        {
            CancelAsync();
        }
        return await t;
    }
}

私はここで完全な回避策についてブログに書きました


4

使用法:

using (var client = new TimeoutWebClient(TimeSpan.FromSeconds(10)))
{
    return await client.DownloadStringTaskAsync(url).ConfigureAwait(false);
}

クラス:

using System;
using System.Net;

namespace Utilities
{
    public class TimeoutWebClient : WebClient
    {
        public TimeSpan Timeout { get; set; }

        public TimeoutWebClient(TimeSpan timeout)
        {
            Timeout = timeout;
        }

        protected override WebRequest GetWebRequest(Uri uri)
        {
            var request = base.GetWebRequest(uri);
            if (request == null)
            {
                return null;
            }

            var timeoutInMilliseconds = (int) Timeout.TotalMilliseconds;

            request.Timeout = timeoutInMilliseconds;
            if (request is HttpWebRequest httpWebRequest)
            {
                httpWebRequest.ReadWriteTimeout = timeoutInMilliseconds;
            }

            return request;
        }
    }
}

しかし、私はより近代的なソリューションをお勧めします:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public static async Task<string> ReadGetRequestDataAsync(Uri uri, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
    using var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
    if (timeout != null)
    {
        source.CancelAfter(timeout.Value);
    }

    using var client = new HttpClient();
    using var response = await client.GetAsync(uri, source.Token).ConfigureAwait(false);

    return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}

OperationCanceledExceptionタイムアウト後にをスローします。


機能しませんでしたが、非同期メソッドは引き続き無期限に機能します
Alex

おそらく問題は異なり、ConfigureAwait(false)を使用する必要がありますか?
Konstantin S.

-1

場合によっては、ユーザーエージェントをヘッダーに追加する必要があります。

WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(myStringWebResource, fileName);
myWebClient.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";

これが私のケースの解決策でした。

クレジット:

http://genjurosdojo.blogspot.com/2012/10/the-remote-server-returned-error-504.html

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.