如何更改.NET WebClient对象上的超时


230

我正在尝试(以编程方式)将客户端的数据下载到本地计算机,并且他们的Web服务器非常非常慢,这导致我的WebClient对象超时。

这是我的代码:

WebClient webClient = new WebClient();

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

有没有办法对此对象设置无限超时?否则,如果没有人可以通过替代方法帮助我举例说明呢?

该URL在浏览器中可以正常工作-仅显示3分钟即可显示。

Answers:


378

您可以延长超时时间:继承原始的WebClient类并重写webrequest getter来设置自己的超时时间,如以下示例所示。

在我的案例中,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 2012年

23
默认超时为100秒。虽然似乎可以运行30秒。
卡特梅德林

2
使用Timespan TimeSpan.FromSeconds(20).Milliseconds设置Timeout稍微容易一些,但是很棒的解决方案!
webwires 2014年

18
@webwires一个人应该使用.TotalMilliseconds而不是.Milliseconds
亚历山大·加尔金

79
班级名称应为: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代替将允许您设置超时。


这是不正确的...您可以在上面看到仍然可以使用WebClient,尽管自定义实现会覆盖WebRequest来设置超时。
DomenicDatti14年

7
“ System.Net.HttpWebRequest.HttpWebRequest()'已过时:此API支持.NET Framework基础结构,不能直接在您的代码中使用”
有用2014年

3
@usefulBee-因为您不应调用该构造函数:"Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."来自msdn.microsoft.com/zh-cn/library/…。另请参见stackoverflow.com/questions/400565/…–
ToolmakerSteve

需要澄清的是:尽管应避免使用此特定的构造方法(无论如何,它不再是较新的.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代替var wresp = (HttpWebResponse)request.GetResponse();,则会再次出现大量超时
andrewjboyd 18'22 Feb'6

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)。

我建议先执行一个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

对于任何需要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)吗?
Konstantin S.

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.