【问题标题】:Adjusting HttpWebRequest Connection Timeout in C#在 C# 中调整 HttpWebRequest 连接超时
【发布时间】:2009-09-30 22:20:48
【问题描述】:

我相信经过长时间的研究和搜索,我发现我想要做的可能是通过设置异步连接并在所需的超时后终止它来更好地服务......但我会继续问无论如何!

快速sn-p代码:

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.

我在一个多线程应用程序中有一个HttpWebRequest 方法,我在其中连接到大量公司网络服务器。在服务器没有响应的情况下,HttpWebRequest.GetResponse() 需要大约 20 秒才能超时,即使我指定的超时时间仅为 5 秒。为了定期通过服务器,我想跳过那些连接时间超过 5 秒的服务器。

所以问题是:“有没有一种简单的方法来指定/减少 WebRequest 或 HttpWebRequest 的连接超时?”

【问题讨论】:

    标签: c# httpwebrequest timeout


    【解决方案1】:

    相信问题在于WebRequest 仅在实际发出请求后才测量时间。如果您向同一个地址提交多个请求,那么ServicePointManager 将限制您的请求,并且仅实际提交与相应ServicePoint.ConnectionLimit 的值一样多的并发连接,默认情况下从ServicePointManager.DefaultConnectionLimit 获取值。应用程序 CLR 主机将此设置为 2,ASP 主机设置为 10。因此,如果您有一个多线程应用程序向同一主机提交多个请求,则实际上只有两个被放置在网络上,其余的将排队。

    我还没有对此进行研究以得出确凿的证据,这是否真的会发生,但在一个类似的项目中,我的情况很糟糕,直到我取消了 ServicePoint 限制。

    另一个需要考虑的因素是 DNS 查找时间。同样,我的信念没有确凿的证据支持,但我认为WebRequest 确实将 DNS 查找时间与请求超时相匹配。在某些部署中,DNS 查找时间可能会成为非常重要的时间因素。

    是的,您必须围绕WebRequest.BeginGetRequestStream(对于带有内容的POSTs)和WebRequest.BeginGetResponse(对于GETs POSTSs)编写您的应用程序。同步调用不会扩展(我不会详细说明原因,但我确实有确凿的证据)。无论如何,ServicePoint 问题与此正交:排队行为也发生在异步调用中。

    【讨论】:

    • 这很有帮助:我相信 DNS 查找时间可能是超出时间发生的地方。一个澄清是,请求都是在不同的线程上向不同的服务器发出的,当所有服务器都响应时,包括连接、下载处理的时间跨度通常小于 4-5 秒。只是当服务器离线时,连接到它的线程需要更多时间才能“意识到”服务器已关闭。
    • 我知道这个问题很老了,但我希望你能看到这个。您知道是否可以更改 DNS 解析超时?在这里查看我的问题:stackoverflow.com/questions/5101507/…。我认为这与这个问题问题非常相似,并且 DNS 解析永远不会超时是有道理的。任何帮助表示赞赏。
    • @mrnye:我评论了那个帖子:有系统范围的 DNS 解析参数:technet.microsoft.com/library/Cc977482
    • @RemusRusanu :不确定您所说的“同步调用不会扩展”是什么意思,这非常模糊。不需要确凿的证据,但你能更具体一点吗?
    • “同步调用不会扩展(我不会详细说明原因,但我确实有确凿的证据)。”...提醒其他人费马大定理?
    【解决方案2】:

    很抱歉添加了一个旧线程,但我认为上面所说的内容可能不正确/具有误导性。

    据我所知,.Timeout 不是连接时间,它是 HttpWebRequest 和响应的整个生命周期所允许的总时间。证明:

    我设置:

    .Timeout=5000
    .ReadWriteTimeout=32000
    

    HttpWebRequest 的连接和发布时间用了 26 毫秒

    但随后的调用 HttpWebRequest.GetResponse() 在 4974 毫秒内超时,因此证明 5000 毫秒是整个发送请求/获取响应调用集的时间限制。

    我没有验证是否将 DNS 名称解析作为时间的一部分进行测量,因为这与我无关,因为这些都不是我真正需要的工作方式——我的目的是在连接时更快地超时到不接受连接的系统,如在请求的连接阶段失败所示。

    例如:我愿意等待有机会返回结果的连接请求 30 秒,但我只想等待 10 秒等待向行为不端的主机发送请求。

    【讨论】:

    • 很好的发现。那么没有简单的方法可以在 HttpWebRequest 上设置连接超时吗?这非常令人沮丧,似乎是一个很大的疏忽......
    【解决方案3】:

    我后来发现有帮助的是.ReadWriteTimeout 属性。除了.Timeout 属性之外,这似乎最终减少了线程尝试从有问题的服务器下载所花费的时间。 .ReadWriteTimeout 的默认时间是 5 分钟,这对我的应用程序来说太长了。

    所以,在我看来:

    .Timeout = 尝试建立连接所花费的时间(不包括查找时间) .ReadWriteTimeout = 建立连接后尝试读取或写入数据所花费的时间

    更多信息:HttpWebRequest.ReadWriteTimeout Property

    编辑:

    根据@KyleM 的评论,Timeout 属性用于整个连接尝试,在 MSDN 上阅读它显示:

    Timeout 是使用 GetResponse 方法发出的后续同步请求等待响应和 GetRequestStream 方法等待流的毫秒数。 超时适用于整个请求和响应,而不是单独适用于 GetRequestStream 和 GetResponse 方法调用。 如果在超时期限内未返回资源,则请求将引发 WebException 并设置 Status 属性到 WebExceptionStatus.Timeout。

    (强调我的。)

    【讨论】:

    • 只是注意到这个答案根本不正确。正如 TechSavvySam 在之前对这个问题的回复中所说,.Timeout 是 FULL 连接请求和响应的限制。如果您的 .Timeout 设置为 5000 毫秒,并且您的数据以 1000 毫秒开始发送,但需要 4999 毫秒才能完全发送,您猜怎么着,您将超时。不管 API 文档怎么说,这都是真实世界的测试所显示的。
    • 我可以凭经验指出,在 .NET 4.0 中,无论文档僵硬说什么,.Timeout 属性似乎并没有真正包含完整的响应。我有一个可重现的远程站点子集,它允许快速建立连接,然后以非常慢的速度运出页面正文数据。尽管我将 .Timeout 设置为 10 秒,但这导致连接总数很容易超过几分钟。但是,只要我.ReadWriteTimeout 设置为 10 秒,那么在 10 秒过后,整个连接就会按需要中止。
    【解决方案4】:

    来自 HttpWebRequest.Timeout 属性的文档:

    域名系统 (DNS) 查询可能 最多需要 15 秒才能返回或 暂停。如果您的请求包含 需要解析的主机名和 您将 Timeout 设置为小于 15 秒,可能需要 15 秒或 更多在抛出 WebException 之前 表示您的请求超时。

    是否有可能是您的 DNS 查询导致超时?

    【讨论】:

    • 这是可能的,但是我使用数据库中的 IP 地址连接到服务器,而不是需要地址查找的 URL。我想如果由于 VPN 关闭而无法访问特定 IP 地址,DNS 仍然可能有问题。当这样的地址无法访问时,我会遇到长时间的超时,只有少数服务器会减慢程序的速度,从而阻止它移动到列表中的其他服务器。
    • 顺便说一下 +1,因为这是一个很好的发现,并且清楚地解释了我之前想知道的 DNS 查找超时。我一定已经阅读了十几遍文档,但不知何故从未见过提到 DNS 15 秒超时。
    【解决方案5】:

    无论我们尝试什么,当我们检查的服务器关闭时,我们都无法将超时时间控制在 21 秒以下。

    为了解决这个问题,我们结合了 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;
    }
    

    【讨论】:

    • 非常感谢!这确实有助于在开始等待连接建立之前确定服务器是否已关闭。节省大量时间。
    猜你喜欢
    • 1970-01-01
    • 2013-01-08
    • 1970-01-01
    • 2010-09-22
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    相关资源
    最近更新 更多