我不知道怎么对付HttpWebRequest.Timeout。以前,我常常为Socket对象设置超时,因为它很简单: Timeout设置发送或接收数据块的最大时间。但是,HttpWebRequest.Timeout似乎为整个超时请求设置了超时。如果请求很大(例如,我使用HTTP PUT上传一个大文件),可能需要几个小时。这导致了我的设置:
...
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秒内吗?对于大文件和慢连接来说,这是一个相当苛刻的限制。
发布于 2016-07-20 12:43:57
在处理大文件上传时,有两个超时问题。HttpWebRequest.Timeout
和HttpWebRequest.ReadWriteTimeout
。我们需要解决这两个问题。
HttpWebRequest.ReadWriteTimeout
首先,让我们来介绍一下HttpWebRequest.ReadWriteTimeout
。我们需要禁用“写流缓冲”。
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步和第3步应用一个较短的超时时间(比如30秒)。我们根本不希望在第2步有一个总的超时时间,但我们确实希望如果字节在一段时间内停止写入,上传就会失败。值得庆幸的是,我们已经用HttpWebRequest.ReadWriteTimeout
解决了第二个问题,我们只需要解决HttpWebRequest.Timeout
令人讨厌的行为。事实证明,GetRequestStream
和GetResponse
的异步版本正是我们所需要的。
所以你想要这段代码:
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);
});
}
}
我们将调用异步版本,而不是调用GetRequestStream
和GetResponse
:
var uploadStream = httpRequest.GetRequestStreamAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
类似地,响应也是这样:
var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
这就是你所需要的。现在,您的上传将更加可靠。您可以将整个上传包装在一个重试循环中,以增加确定性。
发布于 2019-07-09 07:21:42
请注意,对于带有mono的HttpWebRequest,这是非常重要的(如上所述)
httpRequest.AllowWriteStreamBuffering = false;
我花了一整天和Wireshark一起追查为什么大文件上传不成功,最后偶然发现了这个快速、简单的答案。
发布于 2016-04-17 04:52:48
最简单的方法是创建您自己的继承WebRequest的类
public class MyRequest : WebRequest
{
public MyRequest()
{
this.Timeout = 1234;
}
}
https://stackoverflow.com/questions/36668219
复制相似问题