.NET WebClient nesnesindeki zaman aşımı nasıl değiştirilir


230

Bir istemcinin verilerini yerel makineme (programlı olarak) indirmeye çalışıyorum ve web sunucusu çok, çok yavaş, bu da benim nesnemde bir zaman aşımına neden oluyor WebClient.

İşte benim kod:

WebClient webClient = new WebClient();

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

Bu nesne üzerinde sonsuz bir zaman aşımı ayarlamanın bir yolu var mı? Ya da değilse, bunu yapmak için alternatif bir yolla bana bir örnek yardımcı olabilir mi?

URL bir tarayıcıda iyi çalışıyor - gösterilmesi yaklaşık 3 dakika sürüyor.

Yanıtlar:


378

Zaman aşımı süresini uzatabilirsiniz: orijinal WebClient sınıfını devralabilir ve aşağıdaki örnekte olduğu gibi kendi zaman aşımınızı ayarlamak için web isteği alıcısını geçersiz kılabilirsiniz.

Benim durumumda MyWebClient özel bir sınıftı:

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

5
varsayılan zaman aşımı nedir ??
knocte

23
Varsayılan zaman aşımı 100 saniyedir. Her ne kadar 30 saniye boyunca çalışıyor gibi görünüyor.
Carter Medlin

3
Zaman Aşımı'nı bir Zaman Çizelgesi TimeSpan.FromSeconds (20) ile ayarlamak biraz daha kolay.
webwires

18
@webwires Kullanmalı .TotalMillisecondsve kullanmamalı .Milliseconds!
Alexander Galkin

80
Sınıfın adı şöyle olmalıdır: PatientWebClient;)
Jan Willem B

27

İlk çözüm benim için işe yaramadı, ama işte benim için işe yarayan bazı kodlar.

    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

Sen kullanmaya gerek HttpWebRequestziyade WebClientüzerinde zaman aşımı belirleyemezsiniz olarak WebClient(o kullanmasına rağmen bunu uzatmadan HttpWebRequest). Kullanılması HttpWebRequestEğer zaman aşımı ayarlamak için izin verecektir yerine.


Bu doğru değil ... yukarıda, zaman aşımını ayarlamak için WebRequest'i geçersiz kılan özel bir uygulama da olsa WebClient'i kullanabileceğinizi görebilirsiniz.
DomenicDatti

7
"System.Net.HttpWebRequest.HttpWebRequest () 'kullanılmıyor:' Bu API, .NET Framework altyapısını destekler ve doğrudan kodunuzdan kullanılması amaçlanmamıştır"
yararlıBey

3
@usefulBee - Bunu yapıcı çağrı değil, çünkü: "Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."dan msdn.microsoft.com/en-us/library/... . Ayrıca bkz. Stackoverflow.com/questions/400565/…
ToolmakerSteve

Açıklığa kavuşturmak için: Bu özel kurucudan kaçınılması gerekse de (artık yeni .NET sürümlerinin bir parçası değildir), Timeoutözelliğini kullanmak mükemmeldir HttpWebRequest. Milisaniye cinsindendir.
Marcel

10

Ağ kablosunu çıkardığında w.Timeout kodu çalışamadı, zaman aşımına uğramadı, HttpWebRequest kullanarak taşındı ve şimdi işi yapıyor.

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
Bu cevap harika çalışıyor, ancak ilgilenen herkes için var wresp = await request.GetResponseAsync();, var yerine kullanırsanız wresp = (HttpWebResponse)request.GetResponse();tekrar büyük bir zaman aşımı alacaksınız
andrewjboyd

andrewjboyd: neden GetResponseAsync () çalışmadığını biliyor musunuz?
osexpert

9

Tamlık için, kisp'in çözümü VB'ye taşındı (bir yoruma kod ekleyemezsiniz)

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

Sohnee'nin dediği gibi, kullanmak yerine özelliği kullanmak System.Net.HttpWebRequestve ayarlamak .TimeoutSystem.Net.WebClient

Bununla birlikte, sonsuz bir zaman aşımı değeri ayarlayamazsınız (desteklenmez ve bunu yapmaya çalışmak bir atar ArgumentOutOfRangeException).

Önce bir HEAD HTTP isteği gerçekleştirmenizi ve Content-Lengthindirdiğiniz dosyadaki bayt sayısını belirlemek için döndürülen başlık değerini incelemenizi ve ardından sonraki GETistek için zaman aşımı değerini ayarlamanız veya yalnızca çok uzun bir zaman aşımı değeri belirtmenizi öneririm asla aşmayı beklemeyin.


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

Zaman uyumsuz bir zaman aşımına sahip bir WebClient'e ihtiyaç duyan herkes için , önerilen çözümler çalışmaz. İşte işe yarayan:

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;
    }
}

Burada tam çözüm hakkında blog yazdım


4

Kullanımı:

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

Sınıf:

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;
        }
    }
}

Ama daha modern bir çözüm öneriyorum:

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);
}

Bir moladan OperationCanceledExceptionsonra atar .


İşe yaramadı, async yöntemi hala süresiz çalışıyor
Alex

Belki de sorun farklıdır ve ConfigureAwait (false) kullanmanız gerekir?
Konstantin S.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.