【问题标题】:PHP Curl stops after some requestsPHP Curl 在一些请求后停止
【发布时间】:2017-06-19 20:07:40
【问题描述】:

我有一个登录到咖啡机的课程。我有一个 laravel 应用程序,可以“扫描”IP 地址范围内的所有咖啡机。

问题是 Curl 在 39、128 甚至 90 个请求后停止。所以,我不知道是什么问题,或者是否是内存泄漏,因为 PHP 和 Curl 没有显示任何错误。

我需要如何解决此类问题的建议或提示。下面是我的代码。

CoffeeMachine 类

<?php 
namespace Starbucks\CoffeeMachine;
class CoffeeMachine
{
private $url = '';
private $username = '';
private $password = '';

private $session;
private $response;
private $responses;

private $lastMessage = '';
private $lastStatus = FALSE;
private $authenticated = FALSE;

private function setFailStatus($message = '')
{
    $this->lastStatus = FALSE;
    $this->lastMessage = $message;
    return FALSE;
}

private function setSuccessStatus($message = '')
{
    $this->lastStatus = TRUE;
    $this->lastMessage = $message;
    return TRUE;
}

public function __construct($url = '', $username = 'admin', $password = 'admin')
{
    $this->boot();

    $this->url = $url;
    $this->username = $username;
    $this->password = $password;
    $this->session = curl_init();

    curl_setopt($this->session, CURLOPT_CONNECTTIMEOUT, 5);
    curl_setopt($this->session, CURLOPT_FAILONERROR, 1);
    curl_setopt($this->session, CURLOPT_FORBID_REUSE, 1);
    curl_setopt($this->session, CURLOPT_FRESH_CONNECT, 1);
}

public function getResponse()
{
    return $this->response;
}

public function login()
{
    curl_setopt($this->session, CURLOPT_URL, $this->url . '/cgi-bin/dologin');
    curl_setopt($this->session, CURLOPT_POST, 1);
    curl_setopt($this->session, CURLOPT_POSTFIELDS, array(
            'username' => $this->username,
            'password' => $this->password
        )
    );
    curl_setopt($this->session, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($this->session, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($this->session, CURLOPT_HEADER, 0);

    $response = curl_exec($this->session);
    $response = json_decode($response, 1);

    if (!isset($response['response'])) return $this->setFailStatus('Auth with no data...');

    if ($response['response'] != 'success') return $this->setFailStatus('Access denied...');

    $this->response = $response;
    $this->lastStatus = TRUE;
    $this->lastMessage = 'OK';
    $this->authenticated = TRUE;

    return TRUE;
}

public function getDeviceInfo()
{

}

public function logout()
{
    curl_close($this->session);
}
}

在IP范围内发现方法

<?php
public function discover(Request $request)
{
    $from = ip2long($request->input('from', '0.0.0.0'));
    $to = ip2long($request->input('to', '255.255.255.255'));
    $ips = array();

    // CHUNK IN GROUPS OF 10 FOR WAIT 60 SECONDS, BUT NOT WORK
    for($i = $from; $i < $to; $i++) $ips[] = long2ip($i);
    $group_of_ips = array_chunk($ips, 10);

    // TESTED THIS AND NOT WORK
    $default_max_execution_time = ini_get('max_execution_time');
    ini_set('max_execution_time', ((abs($from - $to) * 5) + (count($group_of_ips) * 60)) );

    $machine_ips = array();
    foreach($group_of_ips as $index => $row) {
        foreach($row as $ip) {
            $gs = new CoffeeMachine($ip, 'admin', 'admin');
            if ($gs->login()) {
                $machine_ips[] = $ip;
            }
            $gs->logout();
        }
        sleep(60); // TESTED THIS AND NOT WORK
    }

    ini_set('max_execution_time', $default_max_execution_time);

    /* RETURN THE COFFEE MACHINE IP ADDRESS */
    return $machine_ips;
}

【问题讨论】:

  • 有多少个Ips?你能数出你的脚本可以运行多少秒吗? max_execution_time 可能设置为 60 秒。
  • @EdvardÅkerberg 谢谢,max_execution_time 是根据要发现的 IP 数量计算的,例如 255 个 IP * 5 秒

标签: php laravel curl


【解决方案1】:

添加更多错误检查。我的 curl 包装器 hhb_curl 中的一些 sn-ps:

    $this->curlh = curl_init ( '' ); // why empty string? PHP Fatal error: Uncaught TypeError: curl_init() expects parameter 1 to be string, null given
    if (! $this->curlh) {
        throw new RuntimeException ( 'curl_init failed!' );
    }

在这里我验证 curl_init 是否成功创建了 curl 资源,如果没有,我会抛出 RuntimeException。你也应该这样做。

    $ret = curl_exec ( $this->curlh );
    if ($this->errno ()) {
        throw new RuntimeException ( 'curl_exec failed. errno: ' . var_export ( $this->errno (), true ) . ' error: ' . var_export ( $this->error (), true ) );
    }

在这里,我验证 curl_exec 没有注册任何错误,否则我会抛出 RuntimeException。你也应该这样做。 (它使用 curl_errno($ch) )

function setopt_array(array $options): bool {
    foreach ( $options as $option => $value ) {
        $this->setopt ( $option, $value );
    }
    return true;
}

在这里,我确保我的 setopt_array 实际上没有使用 curl_setopt_array,而是我自己的 curl_setopt 包装器,您应该这样做,原因如下所述。

private function _setopt(int $option, $value): bool {
    $ret = curl_setopt ( $this->curlh, $option, $value );
    if (! $ret) {
        throw new InvalidArgumentException ( 'curl_setopt failed. errno: ' . $this->errno () . '. error: ' . $this->error () . '. option: ' . var_export ( $this->_curlopt_name ( $option ), true ) . ' (' . var_export ( $option, true ) . '). value: ' . var_export ( $value, true ) );
    }
    $this->curloptions [$option] = $value;
    return $ret; // true...
}

在这里我验证 curl_setopt 是否成功,如果没有,我会抛出 InvalidArgumentException,你应该做同样的事情(或者至少抛出某种异常,而不是静默忽略你当前的代码。),不像curl_setopt_array,你实际上可以很容易地确定哪个选项不能设置。

在调试 curl 代码时,始终设置 CURLOPT_VERBOSE。 (hhb_curl 总是这样做,并为 curl 提供 CURLOPT_STDERR tempfile() 以放入详细/错误日志,并通过 $hc->getStdErr() 为您提供日志),您至少应该添加某种形式的 CURLOPT_VERBOSE 支持以进行调试.我的 curl 包装器 hhb_curl 可以在这里找到https://github.com/divinity76/hhb_.inc.php/blob/master/hhb_.inc.php

【讨论】:

    【解决方案2】:

    为 curl 执行添加超时可以解决问题。您需要监控要访问的每个设备的响应时间。对我来说,CONNECTTIMEOUT 和 TIMEOUT 10 秒可以正常工作。

    <?php 
    namespace Starbucks\CoffeeMachine;
    class CoffeeMachine
    {
    private $url = '';
    private $username = '';
    private $password = '';
    
    private $session;
    private $response;
    private $responses;
    
    private $lastMessage = '';
    private $lastStatus = FALSE;
    private $authenticated = FALSE;
    
    private function setFailStatus($message = '')
    {
        $this->lastStatus = FALSE;
        $this->lastMessage = $message;
        return FALSE;
    }
    
    private function setSuccessStatus($message = '')
    {
        $this->lastStatus = TRUE;
        $this->lastMessage = $message;
        return TRUE;
    }
    
    public function __construct($url = '', $username = 'admin', $password = 'admin')
    {
        $this->boot();
    
        $this->url = $url;
        $this->username = $username;
        $this->password = $password;
        $this->session = curl_init();
    
        curl_setopt($this->session, CURLOPT_CONNECTTIMEOUT, 10);
        curl_setopt($this->session, CURLOPT_TIMEOUT, 10);
        curl_setopt($this->session, CURLOPT_FAILONERROR, 1);
        curl_setopt($this->session, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($this->session, CURLOPT_FRESH_CONNECT, 1);
    }
    
    public function getResponse()
    {
        return $this->response;
    }
    
    public function login()
    {
        curl_setopt($this->session, CURLOPT_URL, $this->url . '/cgi-bin/dologin');
        curl_setopt($this->session, CURLOPT_POST, 1);
        curl_setopt($this->session, CURLOPT_POSTFIELDS, array(
                'username' => $this->username,
                'password' => $this->password
            )
        );
        curl_setopt($this->session, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($this->session, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($this->session, CURLOPT_HEADER, 0);
    
        $response = curl_exec($this->session);
    
        if (curl_errno($this->session)) {
            $msg = $this->url . "\n" . curl_errno($this->session) . "\n" . curl_error($this->session);
            return $this->setFailStatus($msg);
        }
    
        $response = json_decode($response, 1);
    
        if (!isset($response['response'])) return $this->setFailStatus('Auth with no data...');
    
        if ($response['response'] != 'success') return $this->setFailStatus('Access denied...');
    
        $this->response = $response;
        $this->lastStatus = TRUE;
        $this->lastMessage = 'OK';
        $this->authenticated = TRUE;
    
        return TRUE;
    }
    
    public function getDeviceInfo()
    {
    
    }
    
    public function logout()
    {
        curl_close($this->session);
    }
    }
    

    现在我可以循环访问 CoffeeMachines =)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多