<?php /** * * PHP version 5.5 * * @author Sergey V.Kuzin <*****@*****.**> * @license MIT */ require __DIR__ . '/../vendor/autoload.php'; $mqtt = new \MQTT\spMQTT('tcp://127.0.0.1:1883/'); \MQTT\spMQTTDebug::Enable(); //$mqtt->setAuth('sskaje', '123123'); $connected = $mqtt->connect(); if (!$connected) { die("Not connected\n"); } $mqtt->ping(); //$msg = str_repeat('1234567890', 209716); $msg = str_repeat('1234567890', 2); # mosquitto_sub -t 'sskaje/#' -q 1 -h test.mosquitto.org $mqtt->publish('sskaje/test', $msg, 0, 1, 0, 1); $msg = str_repeat('привет ', 2); # mosquitto_sub -t 'sskaje/#' -q 1 -h test.mosquitto.org $mqtt->publish('sskaje/test', $msg, 0, 1, 0, 1);
/** * Send packet * * @return int */ public function write() { $this->mqtt->getLogger()->debug('Message write: message_type=' . $this->message_type); $length = 0; $message = $this->build($length); $bytes_written = $this->mqtt->socket_write($message, $length); $this->mqtt->getLogger()->debug('Message write: message=' . spMQTTDebug::printHex($message, true)); $this->mqtt->getLogger()->debug('Message write: bytes written=' . $bytes_written); return $bytes_written; }
/** * loop * @param callback $callback function(spMQTT $mqtt, $topic, $message) * @throws \Exception */ public function loop($callback) { $this->logger->debug(__METHOD__); if (empty($this->topics) && empty($this->topics_to_subscribe)) { $this->logger->critical('No topic subscribed/to be subscribed'); throw new \LogicException('No topic subscribed/to be subscribed', 100601); } $last_subscribe_msgid = 0; $last_subscribe_qos = array(); $last_unsubscribe_msgid = 0; while (1) { # Subscribe topics if (!empty($this->topics_to_subscribe)) { list($last_subscribe_msgid, $last_subscribe_qos) = $this->do_subscribe(); } # Unsubscribe topics if (!empty($this->topics_to_unsubscribe)) { $last_unsubscribe_msgid = $this->do_unsubscribe(); } if (!$this->socket || !$this->checkAndPing()) { $this->logger->error('loop(): EOF detected'); $this->reconnect(); $this->subscribe($this->topics); } $sockets = array($this->socket); $w = $e = null; if (stream_select($sockets, $w, $e, $this->keepalive / 2)) { if (feof($this->socket) || !$this->checkAndPing()) { $this->logger->error('loop(): EOF detected'); $this->reconnect(); $this->subscribe($this->topics); } # The maximum value of remaining length is 268 435 455, FF FF FF 7F. # In most cases, 4 bytes is enough for fixed header and remaining length. # For PUBREL and UNSUBACK, 4 bytes is the maximum length. # For SUBACK, QoS list should be checked. # So, read the first 4 bytes and try to figure out the remaining length, # then read else. # read 4 bytes $read_bytes = 4; $read_message = $this->socket_read($read_bytes); if (empty($read_message)) { continue; } $cmd = $this->unpackCommand(ord($read_message[0])); $message_type = $cmd['message_type']; $dup = $cmd['dup']; $qos = $cmd['qos']; $retain = $cmd['retain']; $this->logger->debug(__METHOD__, ['message_type' => $message_type, 'dup' => $dup, 'QoS' => $qos, 'RETAIN' => $retain]); $flag_remaining_length_finished = 0; for ($i = 1; isset($read_message[$i]); $i++) { if (ord($read_message[$i]) < 0x80) { $flag_remaining_length_finished = 1; break; } } if (empty($flag_remaining_length_finished)) { # read 3 more bytes $read_message .= $this->socket_read(3); } $pos = 1; $len = $pos; $remaining_length = $this->remainingLengthDecode($read_message, $pos); if ($flag_remaining_length_finished) { $to_read = $remaining_length - (3 + $len - $pos); } else { $to_read = $remaining_length - 2; } $this->logger->debug(__METHOD__, ['remaining length' => $remaining_length, 'to read' => $to_read]); $read_message .= $this->socket_read($to_read); $this->logger->debug(__METHOD__, ['message' => spMQTTDebug::printHex($read_message, true)]); switch ($message_type) { # Process PUBLISH case spMQTTMessageType::PUBLISH: $this->logger->debug('loop(): PUBLISH'); # topic length $topic_length = $this->toUnsignedShort(substr($read_message, $pos, 2)); $pos += 2; # topic content $topic = substr($read_message, $pos, $topic_length); $pos += $topic_length; # PUBLISH QoS 0 doesn't have msgid if ($qos > 0) { $msgid = $this->toUnsignedShort(substr($read_message, $pos, 2)); $pos += 2; } # message content $message = substr($read_message, $pos); if ($qos == 0) { $this->logger->debug('loop(): PUBLISH QoS=0 PASS'); # Do nothing } elseif ($qos == 1) { # PUBACK $pubackobj = $this->getMessageObject(spMQTTMessageType::PUBACK); $pubackobj->setDup($dup); $pubackobj->setMsgID($msgid); $puback_bytes_written = $pubackobj->write(); $this->logger->debug('loop(): PUBLISH QoS=1 PUBACK written=' . $puback_bytes_written); } elseif ($qos == 2) { # PUBREC $pubrecobj = $this->getMessageObject(spMQTTMessageType::PUBREC); $pubrecobj->setDup($dup); $pubrecobj->setMsgID($msgid); $pubrec_bytes_written = $pubrecobj->write(); $this->logger->debug('loop(): PUBLISH QoS=2 PUBREC written=' . $pubrec_bytes_written); } else { # wrong packet $this->logger->error('loop(): PUBLISH Invalid QoS'); } # callback call_user_func($callback, $this, $topic, $message); break; # Process PUBREL # Process PUBREL case spMQTTMessageType::PUBREL: $this->logger->debug('loop(): PUBREL'); $msgid = $this->toUnsignedShort(substr($read_message, $pos, 2)); $pos += 2; # PUBCOMP $pubcompobj = $this->getMessageObject(spMQTTMessageType::PUBCOMP); $pubcompobj->setDup($dup); $pubcompobj->setMsgID($msgid); $pubcomp_bytes_written = $pubcompobj->write(); $this->logger->debug('loop(): PUBREL QoS=2 PUBCOMP written=' . $pubcomp_bytes_written); break; # Process SUBACK # Process SUBACK case spMQTTMessageType::SUBACK: $this->logger->debug('loop(): SUBACK'); $msgid = $this->toUnsignedShort(substr($read_message, $pos, 2)); $pos += 2; $qos_list = array(); for ($i = $pos; isset($read_message[$i]); $i++) { # pick bit 0,1 $qos_list[] = ord($read_message[$i]) & 0x3; } # check msg id & qos payload if ($msgid != $last_subscribe_msgid) { $this->logger->debug('loop(): SUBACK message identifier mismatch: ' . $msgid . ':' . $last_subscribe_msgid); } else { $this->logger->debug('loop(): SUBACK msgid=' . $msgid); } if ($last_subscribe_qos != $qos_list) { $this->logger->debug('loop(): SUBACK returned qos list doesn\'t match SUBSCRIBE'); } break; # Process UNSUBACK # Process UNSUBACK case spMQTTMessageType::UNSUBACK: $this->logger->debug('loop(): UNSUBACK'); $msgid = $this->toUnsignedShort(substr($read_message, $pos, 2)); $pos += 2; # TODO:??? if ($msgid != $last_unsubscribe_msgid) { $this->logger->debug('loop(): UNSUBACK message identifier mismatch ' . $msgid . ':' . $last_unsubscribe_msgid); } else { $this->logger->debug('loop(): UNSUBACK msgid=' . $msgid); } break; } } } }