为什么 WebRequest 的超时总是在第一次请求上,而在后续请求中从未发生过?

8

遇到了一个问题,第一次调用WebRequest.GetResponse()会停顿并超时,但在第一次调用后,一切正常。

        try {
            WebRequest myHttpWebRequest = WebRequest.Create(@"http://192.168.x.x/");
            // Sends the HttpWebRequest and waits for the response.         
            myHttpWebRequest.Timeout = 1000;
            WebResponse myHttpWebResponse = myHttpWebRequest.GetResponse();
        } catch(Exception e) {
            Console.WriteLine("Failure 1");
        }
        try {
            WebRequest myHttpWebRequest = WebRequest.Create(@"http://192.168.x.x/");
            // Sends the HttpWebRequest and waits for the response.         
            myHttpWebRequest.Timeout = 1000;
            WebResponse myHttpWebResponse = myHttpWebRequest.GetResponse(); 
        } catch(Exception e) {
            Console.WriteLine("Failure 2");
        }
        try {
            WebRequest myHttpWebRequest = WebRequest.Create(@"http://192.168.x.x/");
            // Sends the HttpWebRequest and waits for the response.         
            myHttpWebRequest.Timeout = 1000;
            WebResponse myHttpWebResponse = myHttpWebRequest.GetResponse(); 
        } catch(Exception e) {
            Console.WriteLine("Failure 3");
        }

在控制台应用程序中使用这段代码时,我总是收到一个 Failure 1 的错误提示,无论是在调试器下还是不在。我已经循环了1000次,它总是在第一次失败,从未在其他时候失败过。实际上,通过阅读 Web 服务器的日志,它实际上从未接收到第一个请求。 我是不是漏掉了什么重要的东西?


你是否已经找到了这个问题的解决方案? - Rahul Jain
1
是的,这是因为VPN正在运行的结果。关闭VPN或类似VPN的软件可以解决问题。 - Rahly
5个回答

10

编辑:我意识到下面的答案适用于完全相反的情况,即第一个请求有效但其他请求无效。 但是,这仍然很重要-您真的应该处理响应。如果在报告错误时也报告异常消息,那将非常有用...

要弄清楚这里发生了什么,您应该使用类似 WireShark 这样的工具,以便您可以查看问题是请求已经发出但没有响应,还是根本没有发出。

我想知道问题是否实际上是正在解析代理或类似的东西...并且刚好有足够的时间在 第二个 请求超时之前解决它。尝试增加超时时间。同样,这应该通过 WireShark 可见。


您没有处理 Web 响应,因此第二个请求的连接池将超时等待获取该连接。

WebResponse 部分放入 using 语句中,您可能会发现它全部正常工作:

using (WebResponse myHttpWebResponse = myHttpWebRequest.GetResponse())
{
}

当然,这是基于你实际上要对响应做些什么的假设。否则,你可以只写:

myHttpWebRequest.GetResponse().Dispose();

:)


我已经报告了异常,它是一个超时。 - Rahly
是的,内部Web服务器始终处于运行状态。实际上,我正在访问日志上执行tail命令,以查看请求的到来情况。但第一个请求从未到达服务器。 - Rahly
@Jeremy:我的意思是将其包含在您的控制台输出中。不过请看一下我的编辑,因为您确实需要查看网络上正在发生的事情。 - Jon Skeet
已经完成了,虽然第一个请求没有发送任何流量到服务器,但第二个和第三个请求都可以正常通过。此外,DNS也不是问题,因为它是通过IP地址的方式访问。在调试器中逐步执行代码并观察流量和访问日志,却没有发现任何问题。 - Rahly
你是正确的,这实际上是VPN的结果。C或Delphi应用程序可以正确处理此问题,没有超时。Internet Explorer和其他Web浏览器也是如此。.NET中是否存在错误,它不使用默认网关,而是盲目尝试VPN? - Rahly
显示剩余6条评论

3

可能有点晚了,但我的情况完全一样。最终的原因是网络中没有默认网关。解决方法是可选择地设置 request.Proxy = null

var request = WebRequest.Create(UriString);
request.Timeout = Timeout;
if (_disableProxy)
{
    request.Proxy = null;
}
if (request is HttpWebRequest)
{
    var response = (HttpWebResponse)request.GetResponse();
    responseStream = response.GetResponseStream();

}
if (request is FtpWebRequest)
{
    var response = (FtpWebResponse)request.GetResponse();
    responseStream = response.GetResponseStream();
}
else if (request is FileWebRequest)
{
    var response = (FileWebResponse)request.GetResponse();
    responseStream = response.GetResponseStream();
}

希望这能有所帮助。

1
晚些回复,但希望能对某些人有所帮助。
我发现我的问题与在新线程中运行HTTP请求有关。由于某种原因,第一个请求总是超时的,之后一切都正常。我可以看到HTTP请求没有命中服务器,这意味着.NET内部的某些内容正在阻止它(记录一下,ServicePointManager.DefaultConnectionLimit不是问题)。
对我来说,解决方案是将请求放入Task而不是Thread中。
(使用.NET Framework 4.x)

我现在遇到了同样的问题。我的页面获取已经在一个线程中了。所以问题是为什么它会表现出这样的行为,因为我不想重构我的代码来使用任务进行请求。 - Robinson

0

我遇到了同样的问题,在我的情况下,我增加了 WebRequest 对象的 timeout 值,然后它就可以工作了!

webRequest.Timeout = int.Parse(60000);

我已将超时属性设置为60秒。


1
听起来更像是超时问题...我将超时设置为3600000,但在12分钟后停止了它... - Rahly
3600000相当巨大。 - Imran Khan Hunzai
我当时正在测试。 - Rahly
祝你好运! - Imran Khan Hunzai
1
我弄明白了...跟VPN有关...停掉VPN后,一切都在第一次尝试时完美地运行了。 - Rahly
1
顺便说一下...我是通过艰难的方式发现这个问题的...超时时间是整个请求的时间...而不仅仅是空闲时间...所以,如果你在慢速连接上发送大量数据,即使它正在积极地发送数据,超时也可能会发生。 - Rahly

0
如果在请求响应之前没有刷新/关闭RequestStream,则可以获得非常类似于此的行为。这种行为似乎存在于.NET 3.5中,但已在.NET Framework 4.5中得到解决。当我切换框架时,我注意到了这个问题 - 在针对3.5编译时,代码(不包括关闭)在4.5中工作时停止工作。也许尝试显式获取RequestStream并将其关闭作为解决方法。

这基本上就是 John 解释的内容,我认为关闭流不是一种“变通方法”,而是一种“必须做到”的做法。 - ForceMagic

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