【问题标题】:HttpWebResponse get stuck while running in a loopHttpWebResponse 在循环运行时卡住
【发布时间】:2023-10-12 20:09:01
【问题描述】:

我构建此方法 (c#) 是为了从 URL 接收 HTTP 响应状态代码。 当我运行这个方法时,它工作正常,但是当我在循环中运行它时,第三次它卡住了。有什么线索吗??

 public static string isAlive(string url)
    {
        Console.WriteLine("start: Is Alive Test");
        WebRequest request = WebRequest.Create(url);
        try
        {
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            return Convert.ToString((int)response.StatusCode);
        }
        catch(WebException ex)
        {
            HttpWebResponse res  = (HttpWebResponse)ex.Response;
            return Convert.ToString((int)res.StatusCode);
        }
    }

循环

        for (int i = 0; i < 5; i++)
        {
            string a = isAlive("https://www.yahoo.com/");
            Console.WriteLine(a);
        }

【问题讨论】:

  • 你确定它卡住了,而不是在等待响应?服务器很有可能会限制请求,因为它可能会检测到可疑活动(在如此短的时间内向服务器抛出如此多的请求似乎是 DoS 攻击)。鉴于您想在不阻塞 UI 线程的情况下发出大量请求,您可能需要考虑转而使用 BeginX/EndX 调用。
  • 定义“卡住”。如果您的程序在 3 次尝试后挂起,则可能是 yahoo 服务器拒绝了您快速连续的 https 请求。另一种选择是您的操作系统正在阻止快速连接到同一地址(内部洪水保护)。编辑 - 答案是另一种选择,您一次打开的连接太多。
  • 您需要将 .GetResponse() 包装到 using 语句中,并出于性能考虑使您的 isAlive 方法异步。请参阅下面的代码示例。
  • @James,我从来没有说过将GetResponse 包装成using 会使其异步。你从哪里得到的?

标签: c# .net webrequest httpwebresponse getresponse


【解决方案1】:

您没有在HttpWebResponse 对象上调用Dispose,这意味着连接仍然存在。如果您将代码更改为以下内容:

public static string isAlive(string url)
{
   Console.WriteLine("start: Is Alive Test");
   WebRequest request = WebRequest.Create(url);
   try
   {
       using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
        {
            return Convert.ToString((int)response.StatusCode);
        }

   }
   catch(WebException ex)
   {
       using(HttpWebResponse res  = (HttpWebResponse)ex.Response)
       {
          return Convert.ToString((int)res.StatusCode);
       }
   }
}

using 语句将为您隐式调用 Dispose,这将关闭连接。

您的代码在第二次迭代后停止的原因是,.Net 有一个内置的最大连接数,它将打开到一个网站,默认情况下为 2。这由 System.Net.ServicePointManager.DefaultConnectionLimit 控制,您可以增加应该你需要。

【讨论】:

  • 我认为你必须为ex.Response做同样的事情。
  • 虽然我同意他们应该用using 包装它,但我怀疑这是问题的原因。 response 对象最终会在超出 isAlive 调用的范围后被释放(虽然不是立即)。这也不应该影响后续调用。
  • @James 假设您为应用程序留有足够的时间让垃圾收集器在每次迭代后进行清理。
  • 该框架使用连接池来处理 HTTP 请求,因此不立即关闭连接可能是导致此问题的原因。
  • @StevenLiekens 实际上我的立场是正确的,根据docs,默认的最大连接数是 2 - 所以这很可能 问题所以 +1。
【解决方案2】:
  • 您需要将 HttpWebResponse var 包装到 using 语句中,因为它是一次性的
  • 在检查 ex.Response.StatusCode 之前,请确保 ex.Status 是 ProtocolError
  • 考虑到性能,还要考虑使您的方法异步
  • 由于您的方法返回状态码,它的名称可能比 isAlive 更好

示例:

public static async Task<string> GetStatusCode(string url)
{
    var request = (HttpWebRequest)WebRequest.Create(url);

    try
    {
        using (var response = (HttpWebResponse)await request.GetResponseAsync())
        {
            return response.StatusCode.ToString();
        }
    }
    catch (WebException ex)
    {
        return ex.Status == WebExceptionStatus.ProtocolError ?
                ((HttpWebResponse)e.Response).StatusCode.ToString() : null;
    }
}

【讨论】:

    【解决方案3】:

    这可能与您没有关闭 HttpWebResponse 有关。 将 finally 添加到关闭响应的 try catch 中。 同时关闭 catch 中的 WebException 响应。

    【讨论】:

      【解决方案4】:

      使用“使用”,它会很好用。

              using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
              {
                  return Convert.ToString((int)response.StatusCode);
              }
      

      【讨论】: