วิธีการเปลี่ยนการหมดเวลาบนวัตถุ. NET WebClient


230

ฉันพยายามดาวน์โหลดข้อมูลลูกค้าไปยังเครื่องท้องถิ่นของฉัน (โดยทางโปรแกรม) และเว็บเซิร์ฟเวอร์ของพวกเขาช้ามากซึ่งทำให้หมดเวลาในWebClientวัตถุของฉัน

นี่คือรหัสของฉัน:

WebClient webClient = new WebClient();

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

มีวิธีการตั้งค่าการหมดเวลาไม่สิ้นสุดกับวัตถุนี้หรือไม่? หรือถ้าไม่มีใครสามารถช่วยฉันด้วยตัวอย่างในวิธีอื่นในการทำเช่นนี้?

URL ทำงานได้ดีในเบราว์เซอร์ - ใช้เวลาแสดงประมาณ 3 นาที

คำตอบ:


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

23
การหมดเวลาเริ่มต้นคือ 100 วินาที แม้ว่ามันจะดูเหมือนว่าจะทำงานเป็นเวลา 30 วินาที
Carter Medlin

3
ง่ายกว่านิดหน่อยในการตั้งค่าการหมดเวลาด้วย Timepan TimeSpan.FromSeconds (20) .Milliseconds ... ทางออกที่ยอดเยี่ยม!
webwires

18
@webwires หนึ่งควรใช้.TotalMillisecondsและไม่.Milliseconds!
Alexander Galkin

80
ชื่อของคลาสควรเป็น: 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 เพื่อตั้งค่าการหมดเวลา
DomenicDatti

7
"System.Net.HttpWebRequest.HttpWebRequest () 'ล้าสมัย:' API นี้รองรับโครงสร้างพื้นฐาน. NET Framework และไม่ได้มีวัตถุประสงค์เพื่อใช้โดยตรงจากรหัสของคุณ"
UsefulBee

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มันมีหน่วยเป็นมิลลิวินาที
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 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และการตั้งค่าสถานที่ให้บริการแทนการใช้TimeoutSystem.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 ด้วยการหมดเวลาที่ใช้งานได้กับวิธีการ async / taskการแก้ปัญหาที่แนะนำจะไม่ทำงาน นี่คือสิ่งที่ทำงาน:

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

ฉัน blogged เกี่ยวกับวิธีแก้ปัญหาแบบเต็มที่นี่


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หลังจากหมดเวลา


ไม่ทำงานวิธีการ
อเล็กซ์

บางทีปัญหาอาจแตกต่างกันและคุณจำเป็นต้องใช้ ConfigureAwait (false)?
Konstantin S.

-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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.