在异步请求中使用超时回调

发布于 2024-12-04 05:11:26 字数 2926 浏览 0 评论 0原文

我之前问过这个问题,但我将用提出的解决方案来完成这个问题,并提出另一个问题。

我正在使用此类来创建异步 WebRequest

class HttpSocket
{
    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
        ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, 1000, true);
        asyncTask.ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            });
    }

    private static void TimeoutCallback(object state, bool timedOut)
    {
        Console.WriteLine("Timeout: " + timedOut);
        if (timedOut)
        {
            Console.WriteLine("Timeout");
            WebRequest request = (WebRequest)state;
            if (state != null)
            {
                request.Abort();
            }
        }
    }
}

我正在使用以下代码测试该类:

class Program
{
    static void Main(string[] args)
    {
        // Making a request to a nonexistent domain.
        HttpSocket.MakeRequest(new Uri("http://www.google.comhklhlñ"), callbackState =>
            {
                if (callbackState.Exception != null)
                    throw callbackState.Exception;
                Console.WriteLine(GetResponseText(callbackState.ResponseStream));
            });
        Thread.Sleep(100000);
    }

    public static string GetResponseText(Stream responseStream)
    {
        using (var reader = new StreamReader(responseStream))
        {
            return reader.ReadToEnd();
        }
    }
}

执行后,立即到达回调,显示“超时:假”并且没有更多抛出,因此超时不起作用。

这是原始线程中提出的解决方案,但是,如您所见,代码为他工作。

我做错了什么?

编辑:代码使用的其他类:

class RequestCallbackState
{
    public Stream ResponseStream { get; private set; }
    public Exception Exception { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }

    public RequestCallbackState(Exception exception)
    {
        Exception = exception;
    }
}

class RequestState
{
    public byte[] RequestBytes { get; set; }
    public WebRequest Request { get; set; }
    public Action<RequestCallbackState> ResponseCallback { get; set; }
}

I asked this question before but I'm going to complete the question with a solution proposed and make another question.

I'm using this class to make an async WebRequest:

class HttpSocket
{
    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
        ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, 1000, true);
        asyncTask.ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            });
    }

    private static void TimeoutCallback(object state, bool timedOut)
    {
        Console.WriteLine("Timeout: " + timedOut);
        if (timedOut)
        {
            Console.WriteLine("Timeout");
            WebRequest request = (WebRequest)state;
            if (state != null)
            {
                request.Abort();
            }
        }
    }
}

And i'm testing the class with this code:

class Program
{
    static void Main(string[] args)
    {
        // Making a request to a nonexistent domain.
        HttpSocket.MakeRequest(new Uri("http://www.google.comhklhlñ"), callbackState =>
            {
                if (callbackState.Exception != null)
                    throw callbackState.Exception;
                Console.WriteLine(GetResponseText(callbackState.ResponseStream));
            });
        Thread.Sleep(100000);
    }

    public static string GetResponseText(Stream responseStream)
    {
        using (var reader = new StreamReader(responseStream))
        {
            return reader.ReadToEnd();
        }
    }
}

Once executed, the callback is reached immediately, showing "Timeout: false" and there aren't more throws, so the timeout isn't working.

This is a solution proposed in the original thread but, as you could see, the code works for him.

What I'm doing wrong?

EDIT: Other classes used by the code:

class RequestCallbackState
{
    public Stream ResponseStream { get; private set; }
    public Exception Exception { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }

    public RequestCallbackState(Exception exception)
    {
        Exception = exception;
    }
}

class RequestState
{
    public byte[] RequestBytes { get; set; }
    public WebRequest Request { get; set; }
    public Action<RequestCallbackState> ResponseCallback { get; set; }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

捂风挽笑 2024-12-11 05:11:26

这种方法有效。我建议将此切换为显式处理异常(包括超时,但也包括错误的域名等),但略有不同。在本例中,我将其分成一个单独的延续。

此外,为了使这一点非常明确,我缩短了超时时间,放入了一个“真实”但缓慢的域,并添加了一个显式的超时状态,您可以看到:

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

class HttpSocket
{
    private const int TimeoutLength = 100;

    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
        ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, TimeoutLength, true);
        asyncTask.ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            }, TaskContinuationOptions.NotOnFaulted);
        // Handle errors
        asyncTask.ContinueWith(task =>
            {
                var exception = task.Exception;
                var webException = exception.InnerException;

                // Track whether you cancelled or not... up to you...
                responseCallback(new RequestCallbackState(exception.InnerException, webException.Message.Contains("The request was canceled.")));
            }, TaskContinuationOptions.OnlyOnFaulted);
    }

    private static void TimeoutCallback(object state, bool timedOut)
    {
        Console.WriteLine("Timeout: " + timedOut);
        if (timedOut)
        {
            Console.WriteLine("Timeout");
            WebRequest request = (WebRequest)state;
            if (state != null)
            {
                request.Abort();
            }
        }
    }
}

class RequestCallbackState
{
    public Stream ResponseStream { get; private set; }
    public Exception Exception { get; private set; }

    public bool RequestTimedOut { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }

    public RequestCallbackState(Exception exception, bool timedOut = false)
    {
        Exception = exception;
        RequestTimedOut = timedOut;
    }
}

class RequestState
{
    public byte[] RequestBytes { get; set; }
    public WebRequest Request { get; set; }
    public Action<RequestCallbackState> ResponseCallback { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // Making a request to a nonexistent domain.
        HttpSocket.MakeRequest(new Uri("http://www.tanzaniatouristboard.com/"), callbackState =>
            {
                if (callbackState.RequestTimedOut)
                {
                    Console.WriteLine("Timed out!");
                }
                else if (callbackState.Exception != null)
                    throw callbackState.Exception;
                else
                    Console.WriteLine(GetResponseText(callbackState.ResponseStream));
            });
        Thread.Sleep(100000);
    }

    public static string GetResponseText(Stream responseStream)
    {
        using (var reader = new StreamReader(responseStream))
        {
            return reader.ReadToEnd();
        }
    }
}

这将运行,并适当地显示超时。

This approach works. I would recommend switching this to explicitly handle exceptions (including your timeout, but also bad domain names, etc) slightly differently. In this case, I've split this into a separate continuation.

In addition, in order to make this very explicit, I've shorted the timeout time, put a "real" but slow domain in, as well as added an explicit timeout state you can see:

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

class HttpSocket
{
    private const int TimeoutLength = 100;

    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
        ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, TimeoutLength, true);
        asyncTask.ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            }, TaskContinuationOptions.NotOnFaulted);
        // Handle errors
        asyncTask.ContinueWith(task =>
            {
                var exception = task.Exception;
                var webException = exception.InnerException;

                // Track whether you cancelled or not... up to you...
                responseCallback(new RequestCallbackState(exception.InnerException, webException.Message.Contains("The request was canceled.")));
            }, TaskContinuationOptions.OnlyOnFaulted);
    }

    private static void TimeoutCallback(object state, bool timedOut)
    {
        Console.WriteLine("Timeout: " + timedOut);
        if (timedOut)
        {
            Console.WriteLine("Timeout");
            WebRequest request = (WebRequest)state;
            if (state != null)
            {
                request.Abort();
            }
        }
    }
}

class RequestCallbackState
{
    public Stream ResponseStream { get; private set; }
    public Exception Exception { get; private set; }

    public bool RequestTimedOut { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }

    public RequestCallbackState(Exception exception, bool timedOut = false)
    {
        Exception = exception;
        RequestTimedOut = timedOut;
    }
}

class RequestState
{
    public byte[] RequestBytes { get; set; }
    public WebRequest Request { get; set; }
    public Action<RequestCallbackState> ResponseCallback { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // Making a request to a nonexistent domain.
        HttpSocket.MakeRequest(new Uri("http://www.tanzaniatouristboard.com/"), callbackState =>
            {
                if (callbackState.RequestTimedOut)
                {
                    Console.WriteLine("Timed out!");
                }
                else if (callbackState.Exception != null)
                    throw callbackState.Exception;
                else
                    Console.WriteLine(GetResponseText(callbackState.ResponseStream));
            });
        Thread.Sleep(100000);
    }

    public static string GetResponseText(Stream responseStream)
    {
        using (var reader = new StreamReader(responseStream))
        {
            return reader.ReadToEnd();
        }
    }
}

This will run, and show a timeout appropriately.

橙味迷妹 2024-12-11 05:11:26

使用 2 个不同的类:

class RequestCallbackException : Exception
{
    public RequestCallbackException(Stream responseStream, Exception exception) : base(exception)
    {
    }
}

并且

class RequestCallbackStream
{
    public Stream ResponseStream { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }
}

您会注意到有时 GetResponseStream() 返回 null,这会立即引发异常

asyncTask.ContinueWith() -->

GetResponseText(callbackState.ResponseStream)-->

using (var reader = new StreamReader(responseStream)) // responseStream is null
{
}

Use 2 different classes:

class RequestCallbackException : Exception
{
    public RequestCallbackException(Stream responseStream, Exception exception) : base(exception)
    {
    }
}

and

class RequestCallbackStream
{
    public Stream ResponseStream { get; private set; }

    public RequestCallbackState(Stream responseStream)
    {
        ResponseStream = responseStream;
    }
}

You will notice that sometimes GetResponseStream() returns null, which immediately raise an exception in

asyncTask.ContinueWith() -->

GetResponseText(callbackState.ResponseStream)-->

using (var reader = new StreamReader(responseStream)) // responseStream is null
{
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文