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

C# 如何在C中为大型HTTP请求设置HttpWebRequest.Timeout#,c#,sockets,http,timeout,C#,Sockets,Http,Timeout,我不知道如何处理HttpWebRequest.Timeout。以前,我常常直接为套接字对象设置超时:Timeout设置发送或接收数据块的最大时间。但是,HttpWebRequest.Timeout似乎设置了整个HTTP请求的超时。如果请求很大(例如,我正在用HTTP PUT上传一个大文件),可能需要几个小时。这让我想到了设置: ... request.Timeout = System.Threading.Timeout.Infinite; Stream requestStream = reque

我不知道如何处理HttpWebRequest.Timeout。以前,我常常直接为套接字对象设置超时:Timeout设置发送或接收数据块的最大时间。但是,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 never抛出“操作超时”异常?所以超时的概念在这种情况下不起作用

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

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


另外,在设置断点时,我发现requestStream实际上是ConnectStream实例,其超时值为300000(300秒)。这难道不意味着无限实际上并不是无限的,而是被限制在300秒之内吗?对于大文件和慢速连接,这是一个相当严格的限制。

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

    public class MyRequest : WebRequest
    {
        public MyRequest()
        {
            this.Timeout = 1234;
        }
    }

在处理大型文件上传时,有两个超时问题困扰着我们
HttpWebRequest.Timeout
HttpWebRequest.ReadWriteTimeout
。我们需要同时解决这两个问题

HttpWebRequest.ReadWriteTimeout

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

更改此设置后,您的
HttpWebRequest.ReadWriteTimeout
值将神奇地按您所希望的方式工作,您可以将其保留在默认值(5分钟)甚至减少。我用60秒

出现此问题的原因是,在上载大文件时,如果数据由.NET framework缓冲,则代码会认为上载已完成,而不是完成,并过早调用
HttpWebRequest.GetResponse()
,这将超时

HttpWebRequest.Timeout

现在,让我们寻址http://code>HttpWebRequest.Timeout

第二个问题是因为
HttpWebRequest.Timeout
适用于整个上传。上载过程实际上包括三个步骤():

  • 启动上载的请求
  • 写入字节
  • 完成上载并从服务器获得任何响应的请求
  • 如果我们有一个适用于整个上传的超时,我们需要大量的时间来容纳上传的大文件,但我们也面临一个问题,即合法的超时需要很长时间才能真正超时。这不是一个好情况。相反,我们希望对步骤1和步骤3应用一个短的超时(比如30秒)。我们根本不希望在#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);
            });
        }
    }
    
    同样,对于回应:

    var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
    

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

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

    httpRequest.AllowWriteStreamBuffering=false


    我花了整整一天时间与Wireshark一起寻找大文件上传不成功的原因,最后偶然发现了这个简单快捷的答案。

    谢谢您的回复。然而,我还不明白这有什么帮助。目前,在.NET CLR中似乎存在一个bug,它使得HttpWebRequest.ReadWriteTimeout和Stream.WriteTimeout变得无用。您的解决方案如何帮助克服这一问题?经理c#不允许您访问这些属性。使用MyRequest并将代码添加到类中。不是很难。我可以设置HttpWebRequest.Timeout。直接在子类中设置相同属性的目的是什么?效果会有什么不同?
    var uploadStream = httpRequest.GetRequestStreamAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
    
    var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;