HttpWebRequest第二次调用超时

47
以下代码第二次(以及随后的)运行为何会超时?
代码在以下处暂停:
using (Stream objStream = request.GetResponse().GetResponseStream())

然后会导致 WebException,表示请求超时。
我已经尝试过使用 WebRequest 和 HttpWebRequest 进行操作。
编辑:代码好像在 request.GetResponse() 处出现问题。
编辑:这篇帖子表明可能是 GC 问题--> http://www.vbforums.com/showthread.php?t=610043 -- 根据该帖子的说法,如果在后台打开 Fiddler,则可以缓解该问题。
服务器存在且可供请求。
    private string GetQLMResponse(string URL)
    {
        HttpWebRequest request = WebRequest.Create(URL) as HttpWebRequest;
        request.Credentials = new NetworkCredential(Settings.Default.LicenseUser, Settings.Default.LicensePassword);
        request.KeepAlive = false;
        request.Timeout = 5000;
        request.Proxy = null;

        // Read stream
        string responseString = String.Empty;
        try
        {
            using (var response = request.GetResponse())
            {
                using (Stream objStream = response.GetResponseStream())
                {
                    using (StreamReader objReader = new StreamReader(objStream))
                    {
                        responseString = objReader.ReadToEnd();
                        objReader.Close();
                    }
                    objStream.Flush();
                    objStream.Close();
                }
                response.Close();
            }
        }
        catch (WebException ex)
        {
            throw new LicenseServerUnavailableException();
        }
        finally
        {
            request.Abort();
            request = null;
            GC.Collect();
        }
        return responseString;
    }

抛出的WebException是:

{"操作已超时"} [System.Net.WebException]: {"操作已超时"} Data: {System.Collections.ListDictionaryInternal} HelpLink: null InnerException: null Message: "操作已超时" Source: "System" StackTrace: " at System.Net.HttpWebRequest.GetResponse()\r\n at IQX.Licensing.License.GetQLMResponse(String URL) in C:\Users\jd\SVN\jd\Products\Development\JAD.Licensing\JAD.Licensing\License.cs:line 373" TargetSite: {System.Net.WebResponse GetResponse()}


更新:好的,以下代码现在可以正常工作。ServicePoint将超时时间设置为接近4分钟。在请求对象上更改ServicePoint.ConnectionLeaseTimeout意味着请求现在在5000ms后被销毁。感谢大家的帮助,也感谢这两个页面:

  1. http://blogs.msdn.com/b/adarshk/archive/2005/01/02/345411.aspx
  2. http://msdn.microsoft.com/en-us/library/6hszazfz(v=VS.80).aspx

    private string GetQLMResponse(string URL)
    {
        HttpWebRequest request = WebRequest.Create(URL) as HttpWebRequest;
        request.Credentials = new NetworkCredential(Settings.Default.LicenseUser, Settings.Default.LicensePassword);
        request.KeepAlive = false;
        request.Timeout = 5000;
        request.Proxy = null;
    
        request.ServicePoint.ConnectionLeaseTimeout = 5000;
        request.ServicePoint.MaxIdleTime = 5000;
    
        // Read stream
        string responseString = String.Empty;
        try
        {
            using (WebResponse response = request.GetResponse())
            {
                using (Stream objStream = response.GetResponseStream())
                {
                    using (StreamReader objReader = new StreamReader(objStream))
                    {
                        responseString = objReader.ReadToEnd();
                        objReader.Close();
                    }
                    objStream.Flush();
                    objStream.Close();
                }
                response.Close();
            }
        }
        catch (WebException ex)
        {
            throw new LicenseServerUnavailableException();
        }
        finally
        {
            request.Abort();
        }
        return responseString;
    }
    

你为什么要打上 gc 的标签? - ChrisWue
1
这篇帖子表明可能存在垃圾回收问题:http://www.vbforums.com/showthread.php?t=610043 - Darbio
1
哦,谢谢您发布更新。修复了我的问题。 - Nic Wise
2
感谢您发布您的解决方案。今天为我节省了一些时间。 - Etch
10个回答

31

在之前的回答之后,我想再补充几件事情。默认情况下,HttpWebRequest只允许与同一主机建立两个连接(这是 HTTP 1.1 的“善意”)。

是的,可以覆盖它,但我不会在这个问题中告诉你如何做,你得问另一个问题 :) 我认为你应该看看这篇文章

我认为你仍然没有完全释放与HttpWebRequest相关的所有资源,所以连接池就会发挥作用,这就是问题所在。除非你确实必须这样做,否则我不会试图打破每个服务器每次只有两个连接的规则。

正如上面的某位作者所指出的那样,Fiddler在这种情况下对你有点不利。

在你的 catch 后加上一个好的finally {}子句,并确保像上面的帖子所述,所有流都被清除,关闭并将请求对象的引用设置为 null。

请告诉我们这是否有所帮助。


1
谢谢。我已经尝试强制使用HTTP 1.0。我已经尝试将请求设置为null。然后执行GC.Collect() - 但都没有成功。 - Darbio
1
@JD,也可以尝试执行netstat -n | find /i "<IP地址>"命令,其中<IP地址>是服务的IP。它被列出了多少次,连接状态如何? - dawebber
1
@JD,尝试在ServicePointManager上关闭TCPKeepAlive,通过设置适当的超时时间等。我仍然认为这里有些奇怪,服务器不符合RFC2616标准,或者前面有某种奇怪的反向代理,或者服务器端存在连接限制。 - dawebber
2
@dawebber:谢谢,我现在已经使用request.ServicePoint类和该类上的设置属性使其工作。这也很有帮助 --> http://msdn.microsoft.com/en-us/library/6hszazfz(v=VS.80).aspx - Darbio
1
@dawebber:服务器是IIS 7.5,并且“应该”兼容。现在通过将超时时间更改为5秒来使其正常工作。感谢您的帮助 :) - Darbio
显示剩余7条评论

24

request.GetResponse()获得的WebResponse对象必须被适当地处理。尝试使用以下代码(删除request.Abort()GC.Collect()调用):

using (var wresponse = request.GetResponse())
{
   using (Stream objStream = wresponse.GetResponseStream())
   {
        // ...
   }
}

编辑:鉴于它仍然无法正常工作,我建议您使用空的Windows应用程序进行测试。这样,您可以隔离出app.config问题或每个主机的最大并发调用量*(您是否在应用程序的其他位置使用了其他webrequest对象来连接此主机;哪些webresponse未被正确处理?)。

希望这能解决您的问题,我已经没有更多想法了!

  • 请参见Jon Skeet的答案此处

谢谢,但这仍然导致相同的问题发生。我已经更新了上面的源代码以反映您的建议。 - Darbio
@SamB:我改口了 - 我在Intelisense中找不到Dispose方法...只是在上面提出异常。 - Darbio
@JD:你没有看到Dispose方法,因为IDisposable接口是显式实现的。 - Sam B
@EricLaw:你能看出我没有关闭响应吗?我怀疑请求可能是唯一可能引起问题的对象,因为它是唯一未被处理的对象。 - Darbio
没有服务器响应超过100毫秒。Fiddler已关闭。响应正在被处理。 - Darbio
显示剩余7条评论

10

正如您所述,将Fiddler在后台运行将缓解该问题。这是因为Fiddler会强制关闭任何响应。延伸Sam B上面的帖子,我会确保响应被关闭,如下所示:

using (var wresponse = request.GetResponse())
{
   using (Stream objStream = wresponse.GetResponseStream())
   {
        // ...
   } 
   wresponse.close();
}

也许将代理设置为 null 会有所帮助,如下所示:

 request.Proxy = Null;

由于.NET框架会自动搜索代理,除非你明确指定。在Fiddler运行时,这种影响将得到缓解,因为直接找到了Fiddler的代理。


很奇怪。我唯一能建议的是来自Phil Haak的修复:http://haacked.com/archive/2004/05/15/http-web-request-expect-100-continue.aspx - Sean Hunter
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Arman Bimatov

5

我也遇到了相同的问题,即使正确处理了所有内容的释放、刷新和关闭,在后续对服务器的请求中仍然出现超时问题。尝试刷新连接组,这对我有用:

myRequest.ServicePoint.CloseConnectionGroup(myRequest.ConnectionGroupName);

此外,请确保您未在应用程序中的其他位置意外创建了其他未正确终止/处理的HttpWebRequest/Request对象,因为这将增加Service Point中连接的数量。

1
我遇到了同样的问题,我解决它是确保在每个创建的请求对象上调用Abort()方法。

0

当添加这个时,与我完成

myRequest.ServicePoint.CloseConnectionGroup(myRequest.ConnectionGroupName);

0

你有没有使用默认名称为WindowsFormsAppN的测试应用程序?我曾经遇到过同样的问题,我花了一周时间进行调试,因为它在我的生产代码中可以工作,但在我正在构建的简单测试解决方案中却不能工作。最终,我确定这种行为是使用默认解决方案名称而不是正确命名的解决方案所特有的。

编辑:我发现我的问题与使用BitDefender作为我的AV软件有关。所有的WindowsFormsAppN程序都被阻止了。


0

对于任何想在VB.NET中使用第二次超时的人:

请确保您一直在使用:

  1. response.close()

  2. response.dispose()

在每次使用您的Web响应之后都要这样做。


0

我阅读了上面标记为答案的帖子,但对我来说并没有真正起作用。 最终我使用using标签重写了代码,并添加了一个CloseConnectionGroup段落,这是其他人在这里建议的。最终代码如下:

System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(imageUrl);
                webRequest.AllowWriteStreamBuffering = true;
                webRequest.Timeout = 30000;
                webRequest.ServicePoint.ConnectionLeaseTimeout = 5000;
                webRequest.ServicePoint.MaxIdleTime = 5000;

                using (System.Net.WebResponse webResponse = webRequest.GetResponse())
                {

                    using (System.IO.Stream stream = webResponse.GetResponseStream())
                    {
                        image = System.Drawing.Image.FromStream(stream);
                    }
                }

                webRequest.ServicePoint.CloseConnectionGroup(webRequest.ConnectionGroupName);
                webRequest = null; 
            }

-3

我将HTTP超时设置为10分钟,这对我很有效。

将超时设置为timeout=infinite需要更长的时间,我的程序会进入挂起模式。


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