ThinkPHP6 集成 MQTT 协议

一、前置准备

  1. MQTT 服务端:需先部署 MQTT 服务器(如 EMQ X、Mosquitto),记录服务端地址、端口(默认 1883)、用户名 / 密码(如有)

二、安装 MQTT 客户端依赖

推荐使用 PHP 生态中成熟的 php-mqtt/client 包(支持 MQTT 3.1/3.1.1/5.0,无需编译扩展),通过 Composer 安装:

composer require php-mqtt/client

三、配置 MQTT 连接信息

在 ThinkPHP6 的 config 目录下新建 mqtt.php 配置文件,统一管理连接参数:
<?php
// config/mqtt.php
return [
    // MQTT 服务端地址
    'host'      => env('MQTT_HOST', '127.0.0.1'),
    // 端口(TCP 默认 1883,SSL 默认 8883)
    'port'      => env('MQTT_PORT', 1883),
    // 客户端 ID(唯一,建议加随机数避免冲突)
    'client_id' => env('MQTT_CLIENT_ID', 'tp6_mqtt_' . uniqid()),
    // 用户名(无则留空)
    'username'  => env('MQTT_USERNAME', ''),
    // 密码(无则留空)
    'password'  => env('MQTT_PASSWORD', ''),
    // 保持连接时间(秒)
    'keep_alive' => 60,
    // 是否启用 SSL/TLS
    'use_ssl'   => false,
    // QoS 等级(0/1/2,按需选择)
    'qos'       => 0,
];

可在 .env 文件中配置实际参数,避免硬编码:

[MQTT]
MQTT_HOST=127.0.0.1
MQTT_PORT=1883
MQTT_USERNAME=zyw
MQTT_PASSWORD=zyw123456

 

四、封装 MQTT 工具类

在 app/common/lib 目录下创建 MqttClient.php,封装连接、发布、订阅等核心方法:
<?php
// app/common/lib/MqttClient.php
namespace app\common\lib;

use PhpMqtt\Client\MqttClient as Client;
use PhpMqtt\Client\ConnectionSettings;
use think\facade\Config;
use think\facade\Log;

class MqttClient
{
    // MQTT 客户端实例
    private static $client = null;

    // 初始化连接
    private static function connect()
    {
        if (self::$client !== null) {
            return self::$client;
        }

        // 读取配置
        $config = Config::get('mqtt');
        //halt($config);
        $host = $config['host'];
        $port = $config['port'];
        $clientId = $config['client_id'];

        // 连接配置
        $connectionSettings = (new ConnectionSettings)
            ->setUsername($config['username'])
            ->setPassword($config['password'])
            ->setKeepAliveInterval($config['keep_alive'])
            ->setUseTls($config['use_ssl']);

        // 创建客户端实例
        self::$client = new Client($host, $port, $clientId);
        try {
            // 连接服务器
            self::$client->connect($connectionSettings, true);
            Log::info('MQTT 连接成功');
        } catch (\Exception $e) {
            Log::error('MQTT 连接失败:' . $e->getMessage());
            self::$client = null;
            throw new \Exception('MQTT 连接失败:' . $e->getMessage());
        }

        return self::$client;
    }

    /**
     * 发布消息
     * @param string $topic 主题
     * @param string $message 消息内容
     * @param int $qos QoS 等级(0/1/2)
     * @param bool $retain 是否保留消息
     * @return bool
     */
    public static function publish(string $topic, string $message, int $qos = null, bool $retain = false)
    {
        $client = self::connect();
        if ($client === null) {
            return false;
        }

        $qos = $qos ?? Config::get('mqtt.qos');
        try {
            // 发布消息
            $client->publish($topic, $message, $qos, $retain);
            // 确保消息发送完成
            $client->loop(true, 100);
            Log::info("MQTT 发布消息成功,主题:{$topic},消息:{$message}");
            return true;
        } catch (\Exception $e) {
            Log::error("MQTT 发布消息失败:" . $e->getMessage());
            return false;
        }
    }

    /**
     * 订阅主题
     * @param string|array $topics 主题(单个字符串或数组)
     * @param callable $callback 回调函数(处理收到的消息)
     * @param int $qos QoS 等级
     */
    public static function subscribe($topics, callable $callback, int $qos = null)
    {
        $client = self::connect();
        if ($client === null) {
            return;
        }

        $qos = $qos ?? Config::get('mqtt.qos');
        try {
            // 订阅主题
            if (is_string($topics)) {
                $client->subscribe($topics, $callback, $qos);
                Log::info("MQTT 订阅主题成功:{$topics}");
            } elseif (is_array($topics)) {
                foreach ($topics as $topic) {
                    $client->subscribe($topic, $callback, $qos);
                    Log::info("MQTT 订阅主题成功:{$topic}");
                }
            }

            // 持续监听消息(阻塞模式,需在命令行运行)
            $client->loop(true);
        } catch (\Exception $e) {
            Log::error("MQTT 订阅失败:" . $e->getMessage());
            throw new \Exception('MQTT 订阅失败:' . $e->getMessage());
        }
    }

    // 关闭连接
    public static function close()
    {
        if (self::$client !== null) {
            try {
                self::$client->disconnect();
                Log::info('MQTT 连接已关闭');
            } catch (\Exception $e) {
                Log::error('MQTT 关闭连接失败:' . $e->getMessage());
            }
            self::$client = null;
        }
    }
}
MqttClient.php

五、核心操作示例

1. 发布 MQTT 消息(控制器 / 业务层)

在任意控制器中调用工具类发布消息:
/app/api/controller/MqttPublish.php
<?php
namespace app\api\controller;
use app\BaseController;

use think\facade\Db;
use think\facade\Session;
use app\common\lib\MqttClient;//Mqtt
//Mqtt-发布
class MqttPublish  extends BaseController
{

    /**
     * 发布 MQTT 消息
     * @return Json
     */
    public function publish(){
        // 主题和消息内容
        $topic = 'wine_test';
        $message = json_encode([
            'time' => date('Y-m-d H:i:s'),
            'content' => 'Hello ThinkPHP6 + MQTT'
        ]);

        // 发布消息
        $result = MqttClient::publish($topic, $message);

        if ($result) {
            //return returnData(1,'消息发布成功'); 
            return json(['code' => 200, 'msg' => '消息发布成功']);
        } else {
            //return returnData(0,'消息发布失败'); 
            return json(['code' => 500, 'msg' => '消息发布失败']);
        }
    }
}

访问 http://你的域名/api/publish 即可触发消息发布。

image

 

2. 订阅 MQTT 消息(命令行脚本)

由于 MQTT 订阅是阻塞式的(需持续监听消息),需通过 ThinkPHP6 的命令行脚本实现:
步骤 1:创建命令行类
在 app/command 目录下创建 MqttSubscribe.php
app/command/MqttSubscribe.php
<?php
// app/command/MqttSubscribe.php
namespace app\command;

use app\common\lib\MqttClient;
use think\console\Command;
use think\console\Input;
use think\console\Output;

class MqttSubscribe extends Command
{
    // 配置命令
    protected function configure()
    {
        $this->setName('mqtt:subscribe')
             ->setDescription('订阅 MQTT 主题并监听消息');
    }

    // 执行命令
    protected function execute(Input $input, Output $output)
    {
        $output->writeln('开始订阅 MQTT 主题...');

        // 订阅主题(可配置多个)
        $topics = [
            'wine_test',
        ];

        // 回调函数:处理收到的消息
        $callback = function ($topic, $message) use ($output) {
            $output->writeln("\n收到消息:");
            $output->writeln("主题:{$topic}");
            $output->writeln("内容:{$message}");
        };

        // 开始订阅(阻塞运行)
        MqttClient::subscribe($topics, $callback);

        // 订阅结束后关闭连接
        MqttClient::close();
        $output->writeln('订阅结束');
    }
}

 

步骤 2:注册命令
在 config/console.php 中注册命令:
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
    // 指令定义
    'commands' => [
        // 若存在类似如下配置,则表示已注册
        'mqtt:subscribe' => app\command\MqttSubscribe::class,
    ],
];
步骤 3:运行订阅脚本
1.查询命令行是否已经注册:
php think list
  • 若输出结果中无mqtt相关的命名空间 / 命令,则说明该命令未注册
  • image

     

项目根目录执行命令行:
php think mqtt:subscribe

有信息则成功

image

 执行发布,看下订阅是否成功

posted @ 2026-01-04 13:55  zhang_you_wu  阅读(5)  评论(0)    收藏  举报