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

标签 c# sockets http timeout

我不知道如何处理 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.TimeoutHttpWebRequest.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):

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

如果我们有一个适用于整个上传的超时,我们需要大量的超时来适应上传大文件,但我们也面临着合法超时需要很长时间才能真正超时的问题。这不是一个好情况。相反,我们想要一个较短的超时时间(比如 30 秒)以应用于步骤 #1 和 #3。我们根本不希望 #2 出现整体超时,但我们确实希望在字节停止写入一段时间后上传失败。值得庆幸的是,我们已经用 HttpWebRequest.ReadWriteTimeout 解决了 #2,我们只需要修复 HttpWebRequest.Timeout 的恼人行为。事实证明,GetRequestStreamGetResponse 的异步版本完全可以满足我们的需求。

所以你需要这段代码:

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;

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

关于c# - 如何在 C# 中为大型 HTTP 请求设置 HttpWebRequest.Timeout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36668219/

相关文章:

c# - MVC 中的数据模板 :- how to define constant in javascript from the c# code

c# - 新窗口中的 Http 响应对象

c# - 通过 HttpWebRequest 的 Web API 授权

c# - 简单事件c#

c# - 为什么我的服务器无法响应来自Web浏览器的客户端请求?

c++ - 远程使用 C++ 模板函数

python - 在 python(或 apache 下的通用 CGI)中处理分块编码的 HTTP POST 请求

c - 使用套接字在qt中支持多客户端的服务器设计

c# - 地址已经在使用Unity Android

http - 从客户端向服务器发送用户名和密码的正确方法