【问题标题】:Why does a cURL connection fail (without error) if no timeout is set?如果没有设置超时,为什么 cURL 连接会失败(没有错误)?
【发布时间】:2016-07-10 20:09:27
【问题描述】:

我有一个 PHP 脚本,它通过 cURL 连接到一个 URL,然后根据返回的 HTTP 状态代码执行一些操作:

$ch = curl_init();
$options = array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL            => $url,
            CURLOPT_USERAGENT      => "What?!?"
);
curl_setopt_array($ch, $options);
$out = curl_exec($ch);
$code = curl_getinfo($ch)["http_code"];
curl_close($ch);

if ($code == "200") {
    echo "200";
} else {
   echo "not 200";
}

一些网络服务器的回复速度很慢,并且虽然页面在几秒钟后加载到我的浏览器中我的脚本在尝试连接到该服务器时告诉我它没有收到肯定(“200”)回复。所以,显然,cURL 发起的连接超时了。

但是为什么呢?我没有在我的脚本中设置超时,根据other answers on this site,cURL 的默认超时肯定比页面加载到我的浏览器所需的三四秒长。

那么为什么连接会超时,如果显然它已经设置为无限,我怎样才能让它持续更长时间?


注意事项:

  • 相同的 URL 并不总是超时。所以有时 cURL 可以连接。
  • 有时超时的不是一个特定的 URL,而是不同时间的不同 URL。
  • 我在共享服务器上,所以我没有任何文件的 root 访问权限。
  • 我尝试查看 curl_getinfo($ch)curl_error($ch) - 根据 @drew010 在 cmets 中的建议 - 但每当问题发生时,两者都是空的。
  • 整个脚本运行了一分钟多一点。这次它成功连接到 300 多个 URL。即使其中一个 URL 失败,其他连接也会成功建立。所以脚本不会超时。
  • cURL 也不会超时,因为当我尝试连接到一个脚本休眠 59 秒的 URL 时,cURL 成功连接。因此,对于 cURL 而言,失败的 URL 的缓慢显然不是问题。

更新

按照@Karlos 在他的回答中的建议,我使用了:

CURLOPT_VERBOSE        => 1,
CURLOPT_STDERR         => $curl_log

(使用来自 this answer 的代码)并在 URL 失败(URL 和 IP 更改)时在 $curl_log 中找到以下内容:

* About to connect() to www.somesite.com port 80 (#0)
*   Trying 104.16.37.249... * connected
* Connected to www.somesite.com (104.16.37.249) port 80 (#0)
GET /wp_german/?feed=rss2 HTTP/1.1
User-Agent: myURL
Host: www.somesite.com
Accept: */*

* Recv failure: Connection reset by peer
* Closing connection #0

所以,我找到了为什么——谢谢@Karlos! – 显然@Axalix 是对的,这是一个网络问题。我现在将遵循此站点上针对此类失败给出的建议。感谢大家的帮助!

【问题讨论】:

  • 超时需要多长时间?是连接超时还是套接字超时?
  • @Chris 这是一个连接到大约 300 个 URL 的脚本。它通常在一分钟左右完成。我不知道如何检查它是什么样的超时。
  • 您应该转储curl_getinfo($ch); 以查看响应的详细信息。如果响应代码在请求期间从未尝试连接或出现问题(超时除外),则响应代码可能为空。当所有其他方法都失败时,curl_error($ch); 也会返回一条错误消息。
  • 要找出您应该将时间戳写入日志文件的所有时间。这样,您将能够找出失败请求的超时时间。
  • @what 如果没有模式,那么您可能只是面临网络问题。可能是您的提供商、DNS 等。在不同的网络中尝试相同的代码,看看您是否有同样的问题。

标签: php curl timeout


【解决方案1】:

我使用 curl 的经验告诉我,有时在使用该选项时:

CURLOPT_RETURNTRANSFER => true

服务器可能不会给出成功的回复,或者至少在 curl 必须接收响应并缓存它的时间范围内给出成功的回复,因此 curl 会将结果返回到您分配的变量。在您的代码中:

$out = curl_exec($ch);

在这个stackoverflow 问题CURLOPT_RETURNTRANSFER set to true doesnt work on hosting server 中,您可以看到选项CURLOPT_RETURNTRANSFER 直接受请求的主机Web 服务器实现的影响。

由于您明确使用响应正文,并且您的代码依赖于响应标头,因此解决此问题的一个好方法可能是:

CURLOPT_RETURNTRANSFER => false

并执行 curl 代码以处理响应标头。

一旦你有了你感兴趣的代码的标题,你就可以运行一个 php 脚本来响应 curl 响应并自己解析它:

<?php
    $url=isset($_GET['url']) ? $_GET['url'] : 'http://www.example.com';
    $ch= curl_init();
    $options = array(
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_URL            => $url,
            CURLOPT_USERAGENT      => "myURL"
    );
    curl_setopt_array($ch, $options);
    curl_exec($ch);
    curl_close($ch);
?>

无论如何,对您的问题的回复为什么您的请求没有收到错误,我猜想使用选项 CURLOPT_NOSIGNAL 和解释的不同超时选项set_opt php manual 可能会让你更接近它。

为了进一步挖掘,选项 CURLOPT_VERBOSE 可能会帮助您通过 STDERR 获得有关请求行为的额外信息。

【讨论】:

  • 感谢您的热心帮助!找到了“为什么”(请参阅​​我的问题的更新),现在将尝试根据这种理解来工作。
【解决方案2】:

原因可能是您的托管服务提供商对传出连接施加了一些限制。

以下是保护脚本的方法:

  1. 在数据库中创建一个包含所有需要获取的 URL 的队列。

  2. 每分钟或 5 分钟运行一次 cron,从 DB 中获取一些 URL - 将它们标记为 in progress

  3. 尝试获取这些 URL。在 DB 中将每个获取的 URL 标记为 success

  4. 增加不成功的失败计数。

  5. 继续遍历队列,直到队列为空。

如果您实施这样的解决方案,您将能够在任何不利条件下处理每个 URL。

【讨论】:

  • 我不确定您是否正确阅读了我的问题。我的脚本没有失败。它结束了。失败的是连接到 300 多个 URL 中的一个。它不是最后一个 URL。在那个 URL 失败后,我的脚本继续运行。我想要了解为什么此 URL 在我的脚本中失败,当它在浏览器中没有失败以及我的脚本何时可以处理更长的延迟(59 秒) 这个 URL(3 秒)。
  • 如果这只是一个看起来很奇怪的 URL。您可能需要调试它才能理解。如果你能提供我可以看看。顺便说一句,您是否尝试从不同的服务器运行相同的脚本?
  • 它并不总是同一个URL,所有有时失败的URL我都可以在其他时间成功获取。
  • 所以我的解决方案会奏效。原因可能不同。如果它没有一直失败 - 如果没有对问题进行实际调试,就很难说出任何事情。最后一个猜测是在连续提取之间添加超时。就像获取 20 个 URL,等待 30 秒,继续。
  • 是的,您的“解决方案”会起作用。但是,如果没有您的解决方案,我的脚本也是如此!您的“解决方案”并不能解决问题。它所做的只是尝试在另一个时间连接到失败的 URL,我已经通过每小时将我的脚本作为 cronjob 运行来做到这一点。我想要了解 URL 失败的原因,而您的回答并未提供这种理解。请阅读问题!它在标题中询问:“Why ... ?”。
猜你喜欢
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 2016-05-06
  • 2019-04-09
  • 2013-07-16
  • 1970-01-01
  • 2021-04-07
  • 2012-10-06
相关资源
最近更新 更多