简体   繁体   English

在C#中调整HttpWebRequest连接超时

[英]Adjusting HttpWebRequest Connection Timeout in C#

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. 我有一个HttpWebRequest方法,它位于多线程应用程序中,我连接到大量的公司Web服务器。 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. 如果服务器没有响应, HttpWebRequest.GetResponse()大约需要20秒才能超时,即使我指定的超时时间仅为5秒。 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. 为了定期通过服务器,我想跳过连接时间超过5秒的服务器。

So the question is: "Is there a simple way to specify/decrease a connection timeout for a WebRequest or HttpWebRequest?" 所以问题是: “是否有一种简单的方法来指定/减少WebRequest或HttpWebRequest的连接超时?”

I believe that the problem is that the WebRequest measures the time only after the request is actually made. 认为问题在于WebRequest仅在实际发出请求后才测量时间。 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 . 如果您向同一地址提交多个请求,那么ServicePointManager将限制您的请求,并且实际上只提交与相应ServicePoint.ConnectionLimit的值一样多的并发连接,默认情况下,它从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. 应用程序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. 另一个要考虑的因素是DNS查找时间。 Again, is my belief not backed by hard evidence, but I think the WebRequest does not count the DNS lookup time against the request timeout. 同样,我相信没有确凿的证据支持,但我认为WebRequest 计入请求超时的DNS查找时间。 DNS lookup time can show up as very big time factor on some deployments. DNS查找时间可能会在某些部署中显示为非常大的时间因素。

And yes, you must code your app around the WebRequest.BeginGetRequestStream (for POST s with content) and WebRequest.BeginGetResponse (for GET s and POSTS s). 是的,您必须围绕WebRequest.BeginGetRequestStream (对于带内容的POST )和WebRequest.BeginGetResponse (对于GET POSTS )编写应用程序。 Synchronous calls will not scale (I won't enter into details why, but that I do have hard evidence for). 同步调用不会扩展(我不会详细说明为什么,但我确实有确凿的证据)。 Anyway, the ServicePoint issue is orthogonal to this: the queueing behavior happens with async calls too. 无论如何, ServicePoint问题与此正交:排队行为也发生在异步调用上。

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. 从我所知道的.Timeout不是连接时间,它是HttpWebRequest和响应的整个生命周期所允许的总时间。 Proof: 证明:

I Set: 我设置:

.Timeout=5000
.ReadWriteTimeout=32000

The connect and post time for the HttpWebRequest took 26ms HttpWebRequest的连接和发布时间为26ms

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()在4974ms超时,从而证明5000ms是整个发送请求/获取响应调用集的时间限制。

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秒钟等待向一个行为不端的主机发送请求。

Something I found later which helped, is the .ReadWriteTimeout property. 我后来发现有帮助的东西是.ReadWriteTimeout属性。 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. 除了.Timeout属性之外,这似乎最终减少了线程花在尝试从有问题的服务器上下载的时间。 The default time for .ReadWriteTimeout is 5 minutes, which for my application was far too long. .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: 根据Per @ 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. Timeout是使用GetResponse方法进行的后续同步请求等待响应的毫秒数,GetRequestStream方法等待流。 The Timeout applies to the entire request and response, not individually to the GetRequestStream and GetResponse method calls. Timeout适用于整个请求和响应,而不是单独应用于GetRequestStream和GetResponse方法调用。 If the resource is not returned within the time-out period, the request throws a WebException with the Status property set to WebExceptionStatus.Timeout. 如果在超时期限内未返回资源,则该请求将抛出WebException,并将Status属性设置为WebExceptionStatus.Timeout。

(Emphasis mine.) (强调我的。)

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. 域名系统(DNS)查询最多可能需要15秒才能返回或超时。 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. 如果您的请求包含需要解析的主机名,并且您将Timeout设置为小于15秒的值,则可能需要15秒或更长时间才能引发WebException以指示请求超时。

Is it possible that your DNS query is the cause of the timeout? 您的DNS查询是否可能是超时的原因?

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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