【问题标题】:How to define a more aggressive timeout for HttpWebRequest?如何为 HttpWebRequest 定义更激进的超时?
【发布时间】:2012-12-12 10:49:54
【问题描述】:

在可移植类库中,我有以下将数据发布到特定 URL 的方法。该方法效果很好。但是我想指定一个更激进的超时(默认为 100 秒)。

考虑到可移植类库中的 HttpWebRequest 类没有 Timeout 属性,如果调用时间超过几秒钟,我如何确保调用被放弃?

public async Task<HttpResponse> PostAsync(Uri uri, string data)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";

    using (Stream requestStream = await request.GetRequestStreamAsync())
    {
        byte[] postBytes = Encoding.UTF8.GetBytes(data);
        requestStream.Write(postBytes, 0, postBytes.Length);
    }

    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
    return new HttpResponse(response.StatusCode, await new StreamReader(response.GetResponseStream()).ReadToEndAsync());
}

【问题讨论】:

  • HttpWebRequest.Timeout = 20000; ?这够聪明吗?

标签: c# .net httpwebrequest async-await portable-class-library


【解决方案1】:

如果超时,下面的代码将返回一个 HttpWebResponse 或 null。

HttpWebResponse response = await TaskWithTimeout(request.GetResponseAsync(), 100);
if(response != null)
{
  ....
}

Task<HttpWebResponse> TaskWithTimeout(Task<WebResponse> task, int duration)
{
    return Task.Factory.StartNew(() =>
    {
        bool b = task.Wait(duration);
        if (b) return (HttpWebResponse)task.Result;
        return null;
    });
}

--编辑--

创建一个扩展方法会更好

public static class SOExtensions
{
    public static Task<T> WithTimeout<T>(this Task<T> task, int duration)
    {
        return Task.Factory.StartNew(() =>
        {
            bool b = task.Wait(duration);
            if (b) return task.Result;
            return default(T);
        });
    }
}

用法如下:

var response = (HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000);

--编辑 2--

另一种方法

public async static Task<T> WithTimeout<T>(this Task<T> task, int duration)
{
    var retTask = await Task.WhenAny(task, Task.Delay(duration))
                            .ConfigureAwait(false);

    if (retTask is Task<T>) return task.Result;
    return default(T);
}

【讨论】:

  • 哇!有趣的想法!我喜欢它,因为它优雅且相对容易理解。但我想知道 task.Wait 是否是一个阻塞操作(即一个线程将被搁置直到它返回)。
  • @Martin 是的,它是一个阻塞操作,但它是在另一个任务中执行的(参见:Task.Factory.StartNew),与调用 await request.GetResponseAsync() 没有什么不同
  • 我同意!它只是 ThreadPool 上的另一个线程。我想知道我们是否可以使它成为非阻塞的。屏蔽与否,这是一个非常聪明的解决方案。
  • 这是一个非常糟糕的主意,原因有两个。 1. 每次调用都使用一个线程。如果您有 1000 个并行调用,您将有 1000 个线程处于等待状态。我个人使用这种技术将系统推向地面。 2. 当操作“超时”时,Web 请求和底层 TCP 连接永远保持打开状态。我个人正是使用这种技术驱动了另一个系统:)。
  • @Alon-Catz 1. 它为每个呼叫使用一个任务。任务不是线程。 TPL 将尝试将任务分配给正在使用的系统的最佳线程数。 2. 我同意 WebResponse 需要妥善处理。这并不意味着这是一个糟糕的解决方案。只需添加一条 using 语句:using(var response = (HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000)) { }
【解决方案2】:
// Abort the request if the timer fires. 
private static void TimeoutCallback(object state, bool timedOut) { 
    if (timedOut) {
        HttpWebRequest request = state as HttpWebRequest;
            if (request != null) {
              request.Abort();
        }
    }
}

是的,客户端应用程序有责任实现自己的超时机制。您可以从上面的代码中执行此操作,该代码设置超时并使用 ThreadPool.RegisterWaitForSingleObject 方法。有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx。本质上,它会从 GetResponse、BeginGetResponse、EndGetResponse、GetRequestStream、BeginGetRequestStream 或 EndGetRequestStream 中止。

【讨论】:

    猜你喜欢
    • 2014-10-31
    • 1970-01-01
    • 2011-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多