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


231

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

Вот мой код:

WebClient webClient = new WebClient();

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

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

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

Ответы:


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

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

7

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

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

Я бы рекомендовал сначала выполнить HTTP- запрос HEAD и проверить 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

Для тех, кому нужен 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;
    }
}

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


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.