在 C# 中调整 HttpWebRequest 连接超时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1500955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Adjusting HttpWebRequest Connection Timeout in C#
提问by JYelton
I believe after lengthy research and searching, I have discovered that what I want to do is probably better served by setting up an asynchronous connection and terminating it after the desired timeout... But I will go ahead and ask anyway!
我相信经过长时间的研究和搜索,我发现通过设置异步连接并在所需的超时后终止它可能会更好地满足我想要做的事情......但无论如何我都会继续问!
Quick snippet of code:
快速代码片段:
HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(url);
webReq.Timeout = 5000;
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
// this takes ~20+ sec on servers that aren't on the proper port, etc.
I have an HttpWebRequest
method that is in a multi-threaded application, in which I am connecting to a large number of company web servers. In cases where the server is not responding, the HttpWebRequest.GetResponse()
is taking about 20 seconds to time out, even though I have specified a timeout of only 5 seconds. In the interest of getting through the servers on a regular interval, I want to skip those taking longer than 5 seconds to connect to.
我有一个HttpWebRequest
在多线程应用程序中的方法,我在其中连接到大量公司 Web 服务器。在服务器没有响应的情况下HttpWebRequest.GetResponse()
,即使我指定的超时时间仅为 5 秒,也需要大约 20 秒才能超时。为了定期访问服务器,我想跳过连接时间超过 5 秒的服务器。
So the question is: "Is there a simple way to specify/decrease a connection timeout for a WebRequest or HttpWebRequest?"
所以问题是:“有没有一种简单的方法来指定/减少 WebRequest 或 HttpWebRequest 的连接超时?”
采纳答案by Remus Rusanu
I believethat the problem is that the WebRequest
measures the time only after the request is actually made. If you submit multiple requests to the same address then the ServicePointManager
will throttle your requests and only actually submit as many concurrent connections as the value of the corresponding ServicePoint.ConnectionLimit
which by default gets the value from ServicePointManager.DefaultConnectionLimit
. Application CLR host sets this to 2, ASP host to 10. So if you have a multithreaded application that submits multiple requests to the same host only two are actually placed on the wire, the rest are queued up.
我认为问题在于WebRequest
只有在实际提出请求后才测量时间。如果您向同一个地址提交多个请求,那么ServicePointManager
它将限制您的请求,并且只实际提交与相应值一样多的并发连接ServicePoint.ConnectionLimit
,默认情况下从ServicePointManager.DefaultConnectionLimit
. 应用程序 CLR 主机将此设置为 2,ASP 主机设置为 10。因此,如果您有一个向同一主机提交多个请求的多线程应用程序,实际上只有两个请求在线上,其余的则排队。
I have not researched this to a conclusive evidence whether this is what really happens, but on a similar project I had things were horrible until I removed the ServicePoint
limitation.
我还没有对此进行研究以确定这是否真的发生了确凿的证据,但在一个类似的项目中,我的事情很糟糕,直到我取消了ServicePoint
限制。
Another factor to consider is the DNS lookup time. Again, is my belief not backed by hard evidence, but I think the WebRequest
does notcount the DNS lookup time against the request timeout. DNS lookup time can show up as very big time factor on some deployments.
另一个需要考虑的因素是 DNS 查找时间。同样,我相信没有确凿的证据支持,但我认为WebRequest
不会不计入请求超时的DNS查找时间。在某些部署中,DNS 查找时间可能会显示为非常大的时间因素。
And yes, you must code your app around the WebRequest.BeginGetRequestStream
(for POST
s with content) and WebRequest.BeginGetResponse
(for GET
s andPOSTS
s). Synchronous calls will not scale (I won't enter into details why, but that I dohave hard evidence for). Anyway, the ServicePoint
issue is orthogonal to this: the queueing behavior happens with async calls too.
是的,您必须围绕WebRequest.BeginGetRequestStream
(for POST
s with content) and WebRequest.BeginGetResponse
(for GET
s andPOSTS
s)编写应用程序。同步调用不会扩展(我不会详细说明原因,但我确实有确凿的证据)。无论如何,ServicePoint
问题与此正交:排队行为也发生在异步调用中。
回答by JYelton
Something I found later which helped, is the .ReadWriteTimeout
property. This, in addition to the .Timeout
property seemed to finally cut down on the time threads would spend trying to download from a problematic server. The default time for .ReadWriteTimeout
is 5 minutes, which for my application was far too long.
我后来发现有帮助的东西是.ReadWriteTimeout
财产。除了该.Timeout
属性之外,这似乎最终减少了线程尝试从有问题的服务器下载所花费的时间。默认时间为.ReadWriteTimeout
5 分钟,这对于我的应用程序来说太长了。
So, it seems to me:
所以,在我看来:
.Timeout
= time spent trying to establish a connection (not including lookup time)
.ReadWriteTimeout
= time spent trying to read or write data after connection established
.Timeout
= 尝试建立连接所花费的时间(不包括查找时间)
.ReadWriteTimeout
= 建立连接后尝试读取或写入数据所花费的时间
More info: HttpWebRequest.ReadWriteTimeout Property
更多信息:HttpWebRequest.ReadWriteTimeout 属性
Edit:
编辑:
Per @KyleM's comment, the Timeout
property is for the entire connection attempt, and reading up on it at MSDN shows:
根据@KyleM 的评论,该Timeout
属性用于整个连接尝试,并在 MSDN 上阅读它显示:
Timeout is the number of milliseconds that a subsequent synchronous request made with the GetResponse method waits for a response, and the GetRequestStream method waits for a stream. The Timeout applies to the entire request and response, not individually to the GetRequestStream and GetResponse method calls.If the resource is not returned within the time-out period, the request throws a WebException with the Status property set to WebExceptionStatus.Timeout.
超时是使用 GetResponse 方法发出的后续同步请求等待响应的毫秒数,以及 GetRequestStream 方法等待流的毫秒数。超时适用于整个请求和响应,而不是单独适用于 GetRequestStream 和 GetResponse 方法调用。如果资源未在超时期限内返回,则请求将引发 WebException,并将 Status 属性设置为 WebExceptionStatus.Timeout。
(Emphasis mine.)
(强调我的。)
回答by GBegen
From the documentation of the HttpWebRequest.Timeout property:
从 HttpWebRequest.Timeout 属性的文档中:
A Domain Name System (DNS) query may take up to 15 seconds to return or time out. If your request contains a host name that requires resolution and you set Timeout to a value less than 15 seconds, it may take 15 seconds or more before a WebException is thrown to indicate a timeout on your request.
域名系统 (DNS) 查询最多可能需要 15 秒才能返回或超时。如果您的请求包含需要解析的主机名,并且您将 Timeout 设置为小于 15 秒的值,则可能需要 15 秒或更长时间才能引发 WebException 以指示您的请求超时。
Is it possible that your DNS query is the cause of the timeout?
您的 DNS 查询是否可能是超时的原因?
回答by TechSavvySam
Sorry for tacking on to an old thread, but I think something that was said above may be incorrect/misleading.
很抱歉附上一个旧线程,但我认为上面所说的内容可能不正确/具有误导性。
From what I can tell .Timeout is NOT the connection time, it is the TOTAL time allowed for the entire life of the HttpWebRequest and response. Proof:
据我所知 .Timeout 不是连接时间,它是 HttpWebRequest 和响应的整个生命周期允许的总时间。证明:
I Set:
我设置:
.Timeout=5000
.ReadWriteTimeout=32000
The connect and post time for the HttpWebRequest took 26ms
HttpWebRequest 的连接和发布时间花费了 26 毫秒
but the subsequent call HttpWebRequest.GetResponse() timed out in 4974ms thus proving that the 5000ms was the time limit for the whole send request/get response set of calls.
但是随后的调用 HttpWebRequest.GetResponse() 在 4974 毫秒内超时,从而证明 5000 毫秒是整个发送请求/获取响应调用集的时间限制。
I didn't verify if the DNS name resolution was measured as part of the time as this is irrelevant to me since none of this works the way I really need it to work--my intention was to time out quicker when connecting to systems that weren't accepting connections as shown by them failing during the connect phase of the request.
我没有验证 DNS 名称解析是否作为时间的一部分进行测量,因为这与我无关,因为这些都没有按照我真正需要的方式工作——我的目的是在连接到系统时更快地超时不接受连接,如在请求的连接阶段失败所示。
For example: I'm willing to wait 30 seconds on a connection request that has a chance of returning a result, but I only want to burn 10 seconds waiting to send a request to a host that is misbehaving.
例如:我愿意在有机会返回结果的连接请求上等待 30 秒,但我只想等待 10 秒等待向行为不端的主机发送请求。
回答by Karl Glennon
No matter what we tried we couldn't manage to get the timeout below 21 seconds when the server we were checking was down.
无论我们尝试什么,当我们检查的服务器关闭时,我们都无法使超时时间低于 21 秒。
To work around this we combined a TcpClient check to see if the domain was alive followed by a separate check to see if the URL was active
为了解决这个问题,我们结合了 TcpClient 检查以查看域是否处于活动状态,然后单独检查以查看 URL 是否处于活动状态
public static bool IsUrlAlive(string aUrl, int aTimeoutSeconds)
{
try
{
//check the domain first
if (IsDomainAlive(new Uri(aUrl).Host, aTimeoutSeconds))
{
//only now check the url itself
var request = System.Net.WebRequest.Create(aUrl);
request.Method = "HEAD";
request.Timeout = aTimeoutSeconds * 1000;
var response = (HttpWebResponse)request.GetResponse();
return response.StatusCode == HttpStatusCode.OK;
}
}
catch
{
}
return false;
}
private static bool IsDomainAlive(string aDomain, int aTimeoutSeconds)
{
try
{
using (TcpClient client = new TcpClient())
{
var result = client.BeginConnect(aDomain, 80, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(aTimeoutSeconds));
if (!success)
{
return false;
}
// we have connected
client.EndConnect(result);
return true;
}
}
catch
{
}
return false;
}