[英]How to set HttpWebRequest.Timeout for a large HTTP request in C#
我不知道如何处理HttpWebRequest.Timeout。 以前,我曾经为简单明了的Socket对象设置超时:超时设置了发送或接收数据块的最大时间。 但是,似乎HttpWebRequest.Timeout设置了整个HTTP请求的超时。 如果请求很大(例如,我正在使用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(),只要每个都不超过.Timeout值即可。
还是我需要实现自己的基于套接字的机制来实现这一目标?
另外,设置断点时,我发现requestStream实际上是具有.Timeout = 300000(300秒)的ConnectStream实例。 这不是说Infinite实际上不是那个Infinite,而是限制为300秒吗? 对于大文件和慢速连接,这是一个相当困难的限制。
在处理大文件上传时有两个超时困扰我们。 HttpWebRequest.Timeout
和HttpWebRequest.ReadWriteTimeout
。 我们需要同时解决这两个问题 。
HttpWebRequest.ReadWriteTimeout
首先,让我们处理HttpWebRequest.ReadWriteTimeout
。 我们需要禁用“写流缓冲”。
httpRequest.AllowWriteStreamBuffering = false;
更改此设置后,您的HttpWebRequest.ReadWriteTimeout
值将如您所愿般神奇地工作,您可以将它们保留为默认值(5分钟),甚至减小它们。 我用60秒。
出现此问题的原因是,当上载大文件时,如果数据由.NET框架缓冲,则您的代码将认为上载未完成时上载了,并过早调用HttpWebRequest.GetResponse()
,这将超时。
HttpWebRequest.Timeout
现在,让我们处理HttpWebRequest.Timeout
。
我们遇到的第二个问题是因为HttpWebRequest.Timeout
适用于整个上载。 上传过程实际上包括三个步骤( 这是我的主要参考文章 )
如果我们有一个超时适用于整个上载,则需要大量来容纳较大文件的上载,但是我们也面临一个问题,即合法的超时将花费很长时间才能真正超时。 这不是一个好情况。 相反,我们希望在短时间内(例如30秒)将时间应用于步骤1和步骤3。 我们根本不希望#2整体超时,但是我们确实希望如果字节停止写入一段时间后上传失败。 幸运的是,我们已经使用HttpWebRequest.ReadWriteTimeout
解决了#2的问题,我们只需要解决HttpWebRequest.Timeout
的令人讨厌的行为。 事实证明, GetRequestStream
和GetResponse
的异步版本完全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;
这就是您所需要的。 现在您的上传将更加可靠。 您可以将整个上传文件包装在重试循环中以增加确定性。
请注意,对于具有mono的 HttpWebRequest,这是非常重要的(如Boinst所述)
httpRequest.AllowWriteStreamBuffering = false;
我花了整整一天的时间在Wireshark追查为什么大文件上传失败的原因,最后偶然发现了这个快速,简单的答案。
最简单的方法是创建自己的继承WebRequest的类
public class MyRequest : WebRequest
{
public MyRequest()
{
this.Timeout = 1234;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.