我不知道如何处理 HttpWebRequest.Timeout。以前,我曾经直接为 Socket 对象设置超时:超时设置发送或接收数据 block 的最长时间。但是,似乎 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(),前提是每个 Write() 都不会超过 .Timeout 值。
或者我是否需要实现我自己的基于 Socket 的机制来实现它?
另外,在设置断点时,我发现requestStream实际上是一个ConnectStream实例,它有.Timeout=300000(300秒)。这是否意味着 Infinite 实际上不是无限的并且限制为 300 秒?对于大文件和慢速连接,这是相当严格的限制。
最佳答案
在处理大文件上传时,有两个超时问题困扰着我们。 HttpWebRequest.Timeout
和 HttpWebRequest.ReadWriteTimeout
。我们需要解决两者。
HttpWebRequest.ReadWriteTimeout
首先,让我们解决 HttpWebRequest.ReadWriteTimeout
。我们需要禁用“写流缓冲”。
httpRequest.AllowWriteStreamBuffering = false;
更改此设置后,您的 HttpWebRequest.ReadWriteTimeout
值将神奇地按照您希望的方式工作,您可以将它们保留为默认值(5 分钟),甚至减少它们。我用 60 秒。
出现这个问题是因为,当上传一个大文件时,如果数据被.NET framework缓冲,你的代码会认为上传已经完成,而实际上并没有,然后调用HttpWebRequest.GetResponse()
太早,会超时。
HttpWebRequest.Timeout
现在,让我们解决 HttpWebRequest.Timeout
。
我们的第二个问题是因为 HttpWebRequest.Timeout
应用于整个上传。上传过程实际上包括三个步骤(here's a great article that was my primary reference):
- 请求开始上传。
- 字节的写入。
- 完成上传并从服务器获得任何响应的请求。
如果我们有一个适用于整个上传的超时,我们需要大量的超时来适应上传大文件,但我们也面临着合法超时需要很长时间才能真正超时的问题。这不是一个好情况。相反,我们想要一个较短的超时时间(比如 30 秒)以应用于步骤 #1 和 #3。我们根本不希望 #2 出现整体超时,但我们确实希望在字节停止写入一段时间后上传失败。值得庆幸的是,我们已经用 HttpWebRequest.ReadWriteTimeout
解决了 #2,我们只需要修复 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;
这就是您所需要的。现在您的上传将更加可靠。您可以将整个上传包装在重试循环中以增加确定性。
关于c# - 如何在 C# 中为大型 HTTP 请求设置 HttpWebRequest.Timeout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36668219/