运行循环时,HttpWebResponse卡住了

4

我编写了这个方法(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调用。 - James
定义“卡住”。如果您的程序在三次尝试后仍然挂起,可能是雅虎服务器拒绝了您快速连续的https请求。另一种可能性是您的操作系统阻止对同一地址进行快速连接(内部洪水保护)。编辑-答案是另一种可能性,您同时打开了太多的连接。 - Adam Kewley
您需要将.GetResponse()放入using语句中,并将isAlive方法异步化以考虑性能问题。请参见下面的代码示例: - Konstantin Tarkus
@James,我从来没有说过将GetResponse包装到using中会使它异步。你是从哪里得到这个想法的? - Konstantin Tarkus
4个回答

10

您没有调用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 做同样的处理。 - Steven Liekens
虽然我同意他们应该用 using 包装它,但我怀疑这不是问题的原因。 response 对象最终会在 isAlive 调用的范围之外被处理掉(虽然不是立即)。也不应影响后续调用。 - James
@James,这是在假设你留足了应用程序的时间,让垃圾收集器在每次迭代后进行清理。 - Steven Liekens
1
该框架使用连接池来处理 HTTP 请求,因此不立即关闭连接可能是这个问题的一个合理原因。 - Steven Liekens
@StevenLiekens,实际上我纠正了一下,根据文档,默认的最大连接数是2 - 所以很可能这就是问题所在,所以+1。 - James

1
  • 您需要将 HttpWebResponse 变量包装在 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;
    }
}

0

使用 "using" 就可以很好地工作。

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

0

这可能与您没有关闭HttpWebResponse有关。 在try catch中添加一个finally来关闭响应。 同时在catch中关闭WebException响应。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接