首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在C#中为大型HTTP请求设置HttpWebRequest.Timeout

如何在C#中为大型HTTP请求设置HttpWebRequest.Timeout
EN

Stack Overflow用户
提问于 2016-04-17 02:44:44
回答 3查看 14.6K关注 0票数 7

我不知道怎么对付HttpWebRequest.Timeout。以前,我常常为Socket对象设置超时,因为它很简单: Timeout设置发送或接收数据块的最大时间。但是,HttpWebRequest.Timeout似乎为整个超时请求设置了超时。如果请求很大(例如,我使用HTTP PUT上传一个大文件),可能需要几个小时。这导致了我的设置:

代码语言:javascript
运行
复制
...
request.Timeout = System.Threading.Timeout.Infinite;
Stream requestStream = request.GetRequestStream();
for (something)
{
  ...
  requestStream.Write(b, 0, b.Length);
}

然而,这不是意味着如果与服务器的网络连接卡住了,我将以requestStream.Write永远不会抛出“操作超时”异常而告终吗?那么超时的概念在这种情况下就不起作用了?

理想情况下,我希望.Timeout设置只影响单个requestStream.Write。我可以根据需要使用任意多的Write(),只要每个Write()都不会超过.Timeout的值。

或者我需要实现我自己的基于套接字的机制来实现这一点?

此外,在设置断点时,我发现requestStream实际上是具有.Timeout=300000 (300秒)的ConnectStream实例。这不是意味着无限实际上并不是无限的,并且被限制在300秒内吗?对于大文件和慢连接来说,这是一个相当苛刻的限制。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-07-20 12:43:57

在处理大文件上传时,有两个超时问题。HttpWebRequest.TimeoutHttpWebRequest.ReadWriteTimeout。我们需要解决这两个问题。

HttpWebRequest.ReadWriteTimeout

首先,让我们来介绍一下HttpWebRequest.ReadWriteTimeout。我们需要禁用“写流缓冲”。

代码语言:javascript
运行
复制
httpRequest.AllowWriteStreamBuffering = false;

更改此设置后,您的HttpWebRequest.ReadWriteTimeout值将按您所希望的那样神奇地工作,您可以将其保留为默认值(5分钟),甚至可以减小它们。我用了60秒。

出现这个问题的原因是,当上传一个大文件时,如果数据是由.NET框架缓冲的,您的代码将认为上传已经完成,并且过早地调用HttpWebRequest.GetResponse(),这将超时。

HttpWebRequest.Timeout

现在,让我们来介绍一下HttpWebRequest.Timeout

我们的第二个问题出现是因为HttpWebRequest.Timeout应用于整个上传。上传过程实际上包括三个步骤(here's a great article that was my primary reference):

  1. 启动字节的upload.
  2. Writing的请求。
  3. 完成上载并从服务器获取任何响应的请求。

如果我们有一个适用于整个上传的超时,我们需要大量的超时来适应上传大文件,但我们也面临着一个问题,即合法的超时将需要很长时间才能真正超时。这不是一个好的情况。相反,我们希望在第1步和第3步应用一个较短的超时时间(比如30秒)。我们根本不希望在第2步有一个总的超时时间,但我们确实希望如果字节在一段时间内停止写入,上传就会失败。值得庆幸的是,我们已经用HttpWebRequest.ReadWriteTimeout解决了第二个问题,我们只需要解决HttpWebRequest.Timeout令人讨厌的行为。事实证明,GetRequestStreamGetResponse的异步版本正是我们所需要的。

所以你想要这段代码:

代码语言:javascript
运行
复制
public static class AsyncExtensions
{
    public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
    {
        return Task.Factory.StartNew(() =>
        {
            var b = task.Wait((int)timeout.TotalMilliseconds);
            if (b) return task.Result;
            throw new WebException("The operation has timed out", WebExceptionStatus.Timeout);
        });
    }
}

我们将调用异步版本,而不是调用GetRequestStreamGetResponse

代码语言:javascript
运行
复制
var uploadStream = httpRequest.GetRequestStreamAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;

类似地,响应也是这样:

代码语言:javascript
运行
复制
var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;

这就是你所需要的。现在,您的上传将更加可靠。您可以将整个上传包装在一个重试循环中,以增加确定性。

票数 15
EN

Stack Overflow用户

发布于 2019-07-09 07:21:42

请注意,对于带有mono的HttpWebRequest,这是非常重要的(如上所述)

httpRequest.AllowWriteStreamBuffering = false;

我花了一整天和Wireshark一起追查为什么大文件上传不成功,最后偶然发现了这个快速、简单的答案。

票数 1
EN

Stack Overflow用户

发布于 2016-04-17 04:52:48

最简单的方法是创建您自己的继承WebRequest的类

代码语言:javascript
运行
复制
    public class MyRequest : WebRequest
    {
        public MyRequest()
        {
            this.Timeout = 1234;
        }
    }
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36668219

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档