Cách thay đổi thời gian chờ trên đối tượng .NET WebClient


230

Tôi đang cố tải dữ liệu của khách hàng về máy cục bộ của mình (theo chương trình) và máy chủ web của họ rất, rất chậm, điều này gây ra thời gian chờ trong WebClientđối tượng của tôi .

Đây là mã của tôi:

WebClient webClient = new WebClient();

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

Có cách nào để thiết lập thời gian chờ vô hạn trên đối tượng này không? Hoặc nếu không ai có thể giúp tôi với một ví dụ về một cách khác để làm điều này?

URL hoạt động tốt trong trình duyệt - chỉ mất khoảng 3 phút để hiển thị.

Câu trả lời:


378

Bạn có thể kéo dài thời gian chờ: kế thừa lớp WebClient ban đầu và ghi đè trình nhận webrequest để đặt thời gian chờ của riêng bạn, như trong ví dụ sau.

MyWebClient là một lớp riêng trong trường hợp của tôi:

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

5
thời gian chờ mặc định là gì ??
knocte

23
Thời gian chờ mặc định là 100 giây. Mặc dù nó dường như chạy trong 30 giây.
Carter Medlin

3
Dễ dàng hơn một chút để đặt Hết thời gian với Timespan TimeSpan.FromSeconds (20) .Milliseconds ... giải pháp tuyệt vời mặc dù!
webwires

18
@webwires Một người nên sử dụng .TotalMillisecondsvà không .Milliseconds!
Alexander Galkin

80
Tên của lớp nên là: PatientWebClient;)
Jan Willem B

27

Giải pháp đầu tiên không hiệu quả với tôi nhưng đây là một số mã phù hợp với tôi.

    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

Bạn cần sử dụng HttpWebRequestthay WebClientvì bạn không thể đặt thời gian chờ WebClientmà không mở rộng nó (mặc dù nó sử dụng HttpWebRequest). Sử dụng HttpWebRequestthay thế sẽ cho phép bạn đặt thời gian chờ.


Điều này không đúng ... bạn có thể thấy ở trên rằng bạn vẫn có thể sử dụng WebClient, mặc dù việc triển khai tùy chỉnh ghi đè lên WebRequest để đặt thời gian chờ.
DomenicDatti

7
"System.Net.HttpWebRequest.HttpWebRequest () 'đã lỗi thời:' API này hỗ trợ cơ sở hạ tầng .NET Framework và không có ý định sử dụng trực tiếp từ mã của bạn"
hữu íchBee

3
@usiouslyBee - bởi vì bạn không nên gọi hàm tạo đó: "Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."từ msdn.microsoft.com/en-us/l Library / . Đồng thời xem stackoverflow.com/questions/400565/
Mạnh

Chỉ cần làm rõ: Mặc dù nhà xây dựng cụ thể này nên tránh (dù sao nó cũng không phải là một phần của các phiên bản .NET mới hơn), việc sử dụng thuộc Timeouttính của nó là hoàn toàn tốt HttpWebRequest. Đó là trong một phần nghìn giây.
Marcel

10

Không thể làm cho mã w.Timeout hoạt động khi rút cáp mạng, nó không hết thời gian, chuyển sang sử dụng HttpWebRequest và thực hiện công việc ngay bây giờ.

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
Câu trả lời này hoạt động rất tốt, nhưng đối với bất kỳ ai quan tâm, nếu bạn sử dụng var wresp = await request.GetResponseAsync();thay vì var, wresp = (HttpWebResponse)request.GetResponse();bạn sẽ nhận được một khoảng thời gian chờ lớn
andrewjboyd

andrewjboyd: bạn có biết tại sao GetResponseAsync () không hoạt động không?
osexpert

9

Để đầy đủ, đây là giải pháp của kisp được chuyển sang VB (không thể thêm mã vào một nhận xét)

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

Như Sohnee nói, sử dụng System.Net.HttpWebRequestvà thiết lập thuộc Timeouttính thay vì sử dụng System.Net.WebClient.

Tuy nhiên, bạn không thể đặt giá trị hết thời gian vô hạn (nó không được hỗ trợ và cố gắng làm như vậy sẽ ném một ArgumentOutOfRangeException).

Trước tiên, tôi khuyên bạn nên thực hiện yêu cầu HTTP HTTP và kiểm tra Content-Lengthgiá trị tiêu đề được trả về để xác định số byte trong tệp bạn đang tải xuống và sau đó đặt giá trị hết thời gian phù hợp cho GETyêu cầu tiếp theo hoặc chỉ cần chỉ định giá trị hết thời gian rất dài mà bạn sẽ không bao giờ mong đợi vượt quá.


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

Đối với bất kỳ ai cần một WebClient có thời gian chờ hoạt động cho các phương thức async / task , các giải pháp được đề xuất sẽ không hoạt động. Đây là những gì hoạt động:

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

Tôi viết blog về cách giải quyết đầy đủ ở đây


4

Sử dụng:

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

Lớp học:

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

Nhưng tôi đề nghị một giải pháp hiện đại hơn:

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

Nó sẽ ném OperationCanceledExceptionsau một thời gian chờ.


Không hoạt động, phương thức async vẫn hoạt động vô thời hạn
Alex

Có lẽ vấn đề là khác nhau và bạn cần sử dụng ConfigureAwait (false)?
Konstantin S.

-1

Trong một số trường hợp, cần thêm tác nhân người dùng vào các tiêu đề:

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

Đây là giải pháp cho trường hợp của tôi.

Tín dụng:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.