php-mqtt/client

Subscribe stuck in loop

m-genser opened this issue · 2 comments

$mqtt->loop(true, true, 10); don't exit the loop after 10 seconds like it's intended. It always exits by PHP max_execution_time instead.

Tested with following code:

<?php
echo "[" . date("Y-m-d H:i:s") . "] Runtime started<br>" . PHP_EOL;

// Load MQTT client
require_once(__DIR__ . "/vendor/autoload.php");

// Define connection
$connectionSettings = (new \PhpMqtt\Client\ConnectionSettings)
	->setUsername("marvin")
    ->setPassword("super#Secret")
    ->setConnectTimeout(3)
    ->setUseTls(true)
    ->setTlsSelfSignedAllowed(true);

$server   = 'some-mqtt.broker.com';
$port     = 8883;
$clientId = 'marvin';

$mqtt = new \PhpMqtt\Client\MqttClient($server, $port, $clientId);
$mqtt->connect($connectionSettings, true);

// Subscribe to MQTT-Topic
$mqtt->subscribe('some/data/#', function ($topic, $message) {
    echo sprintf("Incoming message: [%s]: %s\n<br>", $topic, $message);
}, 0);

// Run loop max 10 seconds
$mqtt->loop(true, true, 10);

// Close connection
$mqtt->disconnect();

echo "[" . date("Y-m-d H:i:s") . "] Runtime closed<br>" . PHP_EOL;

I think you misread the description of the MqttClient::loop() method:

/**
* Runs an event loop that handles messages from the server and calls the registered
* callbacks for published messages.
*
* If the second parameter is provided, the loop will exit as soon as all
* queues are empty. This means there may be no open subscriptions,
* no pending messages as well as acknowledgments and no pending unsubscribe requests.
*
* The third parameter will, if set, lead to a forceful exit after the specified
* amount of seconds, but only if the second parameter is set to true. This basically
* means that if we wait for all pending messages to be acknowledged, we only wait
* a maximum of $queueWaitLimit seconds until we give up. We do not exit after the
* given amount of time if there are open topic subscriptions though.
*
* @param bool $allowSleep
* @param bool $exitWhenQueuesEmpty
* @param int|null $queueWaitLimit
* @return void
* @throws DataTransferException
* @throws MqttClientException
* @throws ProtocolViolationException
*/
public function loop(bool $allowSleep = true, bool $exitWhenQueuesEmpty = false, int $queueWaitLimit = null): void;

The This means there may be no open subscriptions [...] We do not exit after the given amount of time if there are open topic subscriptions though. part is important. A subscription is normally not a short-term thing, but intended to be kept for quite a while.

What you are actually looking for is the example about interrupting the loop with a loop event handler. All you need to do is register a loop event handler, which checks for the elapsed time since starting the loop to determine whether it is time to exit already:

$client->registerLoopEventHandler(function (MqttClient $client, float $elapsedTime) {
    if ($elapsedTime >= 10) {
        $client->interrupt();
    }
});

To shed some light on the 2nd and 3rd parameter of the loop() method:
When publishing messages with QoS 1 or 2, the client needs confirmation from the broker that the message was received/delivered. If no such confirmation is received, the client will retry sending queued messages. To prevent the client from getting stuck in an infinite loop (if delivery of the message is impossible), these parameters can be used to forcefully cancel the retries after some time.

Yes, unfortunately I misunderstood. Thought that was a issue.

Thank you for the solution!