如果没有设置超时,为什么cURL连接会失败(没有错误)


Why does a cURL connection fail (without error) if no timeout is set?

我有一个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启动的连接超时了。

但为什么呢?我在脚本中没有设置超时,根据这个网站上的其他答案,cURL的默认超时肯定比页面在浏览器中加载所需的三四秒长。

那么,如果显然已经设置为无限,为什么欺骗会超时,我如何才能让它持续更长的时间呢?


注:

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

更新

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

CURLOPT_VERBOSE        => 1,
CURLOPT_STDERR         => $curl_log

(使用此答案中的代码),并在URL失败时在$curl_log中发现以下内容(URL和IP已更改):

* 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是对的,这是一个网络问题。我现在将遵循这个网站上给出的关于这种失败的建议。感谢大家的帮助!

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

CURLOPT_RETURNTRANSFER => true

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

$out = curl_exec($ch);

在这个stackoverflow问题中,CURLOPT_RETURNTTRANSFER设置为true在宿主服务器上不起作用,您可以看到选项CURLOPT/RETURNTTRANSfer直接受到请求的宿主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手册中解释的不同超时选项可能会让您更接近它。

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

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

以下是可以确保脚本安全的方法:

  1. 在DB中创建一个包含所有需要提取的URL的队列。

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

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

  4. 增加失败次数。

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

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