Как изменить время ожидания для объекта .NET WebClient

231

Я пытаюсь загрузить данные клиента на мой локальный компьютер (программно), и их веб-сервер работает очень и очень медленно, что приводит к превышению времени ожидания в моем WebClientобъекте.

Вот мой код:

WebClient webClient = new WebClient();

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

Есть ли способ установить бесконечное время ожидания для этого объекта? Или, если не может кто-нибудь помочь мне с примером альтернативного способа сделать это?

URL работает нормально в браузере - это займет около 3 минут, чтобы показать.

Ryall
источник

Ответы:

378

Вы можете продлить время ожидания: наследовать исходный класс WebClient и переопределить метод получения веб-запросов, чтобы установить собственное время ожидания, как в следующем примере.

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 секунд.
Картер Медлин
3
Немного проще установить время ожидания с помощью Timespan TimeSpan.FromSeconds (20). Миллисекунды ... отличное решение, хотя!
webwires
18
@webwires Нужно использовать, .TotalMillisecondsа не .Milliseconds!
Александр Галкин
80
Название класса должно быть: PatientWebClient;)
Ян Виллем 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вместо этого позволит вам установить время ожидания.

Фентон
источник
Это не так ... вы можете видеть выше, что вы все еще можете использовать WebClient, хотя и пользовательскую реализацию, которая переопределяет WebRequest для установки времени ожидания.
DomenicDatti
7
«System.Net.HttpWebRequest.HttpWebRequest ()» устарел: «Этот API поддерживает инфраструктуру .NET Framework и не предназначен для использования непосредственно из вашего кода»
ПолезноеБее
3
@usefulBee - потому что вы не должны вызывать этот конструктор: "Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."с msdn.microsoft.com/en-us/library/… . Также см. Stackoverflow.com/questions/400565/…
ToolmakerSteve
Просто для пояснения: хотя этого конкретного конструктора следует избегать (в любом случае он больше не является частью более новых версий .NET), вполне можно использовать Timeoutсвойство HttpWebRequest. Это в миллисекундах.
Марсель
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);
}
themullet
источник
1
Этот ответ прекрасно работает, но для всех, кто интересуется, если вы используете var wresp = await request.GetResponseAsync();вместо var, wresp = (HttpWebResponse)request.GetResponse();вы снова получите большой тайм-аут
andrewjboyd
andrewjboyd: вы знаете, почему GetResponseAsync () не работает?
osexpert
9

Для полноты, вот решение kisp, портированное на VB (не могу добавить код в комментарий)

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
GlennG
источник
7

Как говорит Sohnee, использование System.Net.HttpWebRequestи установка Timeoutсвойства вместо использования System.Net.WebClient.

Однако вы не можете установить бесконечное значение тайм-аута (это не поддерживается, и попытка сделать это выдает ArgumentOutOfRangeException).

Я бы рекомендовал сначала выполнить HTTP- запрос HEAD и проверить Content-Lengthвозвращаемое значение заголовка, чтобы определить число байтов в загружаемом файле, а затем соответственно установить значение тайм-аута для последующего GETзапроса или просто указать очень длительное значение тайм-аута, которое вы могли бы никогда не ожидай превзойти.

dariom
источник
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

Для тех, кому нужен WebClient с таймаутом, который работает для методов асинхронизации / задач , предложенные решения не будут работать. Вот что работает:

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

Я написал в блоге о полном обходе здесь

Alex
источник
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)?
Константин С.
-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

Антуан
источник