programing

의 시간 제한을 변경하는 방법.NET WebClient 개체

newstyles 2023. 5. 4. 19:44

의 시간 제한을 변경하는 방법.NET WebClient 개체

클라이언트의 데이터를 로컬 컴퓨터에 (프로그램적으로) 다운로드하려고 하는데 웹 서버가 매우 느려서 시간이 초과되었습니다.WebClient물건.

내 코드는 다음과 같습니다.

WebClient webClient = new WebClient();

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

이 개체에 무한 제한 시간을 설정할 수 있는 방법이 있습니까?아니면 다른 방법으로 이 작업을 수행할 수 있는 예를 들어보겠습니다.

URL은 브라우저에서 잘 작동합니다. 표시하는 데 약 3분 정도 걸립니다.

시간 초과를 확장할 수 있습니다. 즉, 원래 WebClient 클래스를 상속하고 웹 요청 getter를 재정의하여 다음 예제와 같이 사용자 자신의 시간 초과를 설정할 수 있습니다.

My WebClient는 제 경우 개인 클래스였습니다.

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

첫 번째 솔루션은 저에게 효과가 없었지만, 여기 저에게 효과가 있었던 코드가 있습니다.

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

사용해야 합니다.HttpWebRequest보다는WebClient시간 제한을 설정할 수 없으므로WebClient연장하지 않고 (비록 그것이 그것을 사용하더라도).HttpWebRequest). 사용합니다.HttpWebRequest대신 시간 초과를 설정할 수 있습니다.

비동기/작업 방법에 사용할 수 있는 시간 제한이 있는 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;
    }
}

여기에 대한 전체 해결 방법에 대해 블로그에 올렸습니다.

W를 가져올 수 없습니다.네트워크 케이블을 꺼냈을 때 작동할 시간 초과 코드입니다. 시간 초과가 아니라 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);
}

완전성을 위해, 여기 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

Sohnee의 말처럼, 사용하고 설정합니다.Timeout사용하는 대신 자산System.Net.WebClient.

그러나 무한 제한 시간 값을 설정할 수는 없습니다(지원되지 않으며 이를 시도하면ArgumentOutOfRangeException).

먼저 HEAD HTTP 요청을 수행하고 다음을 검토하는 것이 좋습니다.Content-Length다운로드 중인 파일의 바이트 수를 결정하고 그에 따라 다음에 대한 시간 초과 값을 설정하기 위해 반환되는 헤더 값GET요청하거나 단순히 초과하지 않을 것으로 예상되는 매우 긴 시간 제한 값을 지정합니다.

'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

용도:

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잠시 후에

저는 어제 이 문제로 싸워야 했고, 커스텀 익스텐션 클래스도 쓰게 되었습니다.

아래 코드를 보고 수락된 답변과 비교해 보면 알 수 있듯이, 좀 더 다재다능한 클래스를 갖기 위해 제안을 조금 더 수정하려고 했습니다. 이렇게 하면 개체를 인스턴스화할 때나 내부를 사용하는 메서드를 사용하기 직전에 정확한 시간 제한을 설정할 수 있습니다.WebRequest핸들러

using System;
using System.Net;

namespace Ryadel.Components.Web
{
    /// <summary>
    /// An extension of the standard System.Net.WebClient
    /// featuring a customizable constructor and [Timeout] property.
    /// </summary>
    public class RyadelWebClient : WebClient
    {
        /// <summary>
        /// Default constructor (30000 ms timeout)
        /// NOTE: timeout can be changed later on using the [Timeout] property.
        /// </summary>
        public RyadelWebClient() : this(30000) { }

        /// <summary>
        /// Constructor with customizable timeout
        /// </summary>
        /// <param name="timeout">
        /// Web request timeout (in milliseconds)
        /// </param>
        public RyadelWebClient(int timeout)
        {
            Timeout = timeout;
        }

        #region Methods
        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest w = base.GetWebRequest(uri);
            w.Timeout = Timeout;
            ((HttpWebRequest)w).ReadWriteTimeout = Timeout;
            return w;
        }
        #endregion

        /// <summary>
        /// Web request timeout (in milliseconds)
        /// </summary>
        public int Timeout { get; set; }
    }
}

제가 그곳에 있는 동안, 저는 또한 기본값을 낮추는 기회를 잡았습니다.Timeout에 가치를 두는.30(초 로단위초로로 지정100내겐 너무 과분해 보였습니다.

이 수업이나 사용법에 대한 추가 정보가 필요한 경우, 제가 블로그에 쓴 이 게시물을 확인하세요.

kisp 솔루션에 따르면 이것은 비동기식으로 작동하는 편집된 버전입니다.

클래스 WebConnection.cs

internal class WebConnection : WebClient
{
    internal int Timeout { get; set; }

    protected override WebRequest GetWebRequest(Uri Address)
    {
        WebRequest WebReq = base.GetWebRequest(Address);
        WebReq.Timeout = Timeout * 1000 // Seconds
        return WebReq;
    }
}

비동기 작업

private async Task GetDataAsyncWithTimeout()
{
    await Task.Run(() =>
    {
        using (WebConnection webClient = new WebConnection())
        {
            webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
            webClient.DownloadData("https://www.yourwebsite.com");
        }
    });
} // await GetDataAsyncWithTimeout()

또는 비동기를 사용하지 않으려면 다음을 수행합니다.

private void GetDataSyncWithTimeout()
{
    using (WebConnection webClient = new WebConnection())
    {
        webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
        webClient.DownloadData("https://www.yourwebsite.com");
    }
} // GetDataSyncWithTimeout()

경우에 따라 헤더에 사용자 에이전트를 추가해야 합니다.

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

언급URL : https://stackoverflow.com/questions/1789627/how-to-change-the-timeout-on-a-net-webclient-object