繁体   English   中英

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

[英]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.TimeoutHttpWebRequest.ReadWriteTimeout 我们需要同时解决这两个问题

HttpWebRequest.ReadWriteTimeout

首先,让我们处理HttpWebRequest.ReadWriteTimeout 我们需要禁用“写流缓冲”。

httpRequest.AllowWriteStreamBuffering = false;

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

出现此问题的原因是,当上载大文件时,如果数据由.NET框架缓冲,则您的代码将认为上载未完成时上载了,并过早调用HttpWebRequest.GetResponse() ,这将超时。

HttpWebRequest.Timeout

现在,让我们处理HttpWebRequest.Timeout

我们遇到的第二个问题是因为HttpWebRequest.Timeout适用于整个上载。 上传过程实际上包括三个步骤( 这是我的主要参考文章

  1. 开始上传的请求。
  2. 字节的写入。
  3. 完成上传并从服务器获得任何响应的请求。

如果我们有一个超时适用于整个上载,则需要大量来容纳较大文件的上载,但是我们也面临一个问题,即合法的超时将花费很长时间才能真正超时。 这不是一个好情况。 相反,我们希望在短时间内(例如30秒)将时间应用于步骤1和步骤3。 我们根本不希望#2整体超时,但是我们确实希望如果字节停止写入一段时间后上传失败。 幸运的是,我们已经使用HttpWebRequest.ReadWriteTimeout解决了#2的问题,我们只需要解决HttpWebRequest.Timeout的令人讨厌的行为。 事实证明, GetRequestStreamGetResponse的异步版本完全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);
        });
    }
}

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM