一、创建自定义指令控制器
php artisan make:command MqttSubscribe
该命令会在app/Console/Commands下生成一个MqttSubscribe.php的类
二、修改MqttSubscribe.php
<?php
namespace App\Console\Commands;
use App\extend\mqtt\MqttServer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
//php artisan make:command MqttSubscribe
/**
* 订阅MQTT消息
* Class MqttSubscribe
* @package App\Console\Commands
*/
class MqttSubscribe extends Command
{
/**
* The name and signature of the console command.
* @var string
*/
protected $signature = 'mqtt:sub';
/**
* The console command description.
* @var string
*/
protected $description = 'MQTT订阅消息';
/**
* 订阅事件
* @var array
*/
public $handleSubFun = [];
/**
* socket
* @var
*/
protected $socket;
public function handle()
{
dump("开启订阅");
$this->bindSubFun();
$this->start();
}
protected function start()
{
$mqttServer = new MqttServer();
$topics = [
'server/v1' => $this->handleSubFun['server/v1'],
];
$mqttServer->subscribe($topics, 0);
while ($mqttServer->proc()) {
}
$mqttServer->close();
}
/**
* 绑定事件
*/
public function bindSubFun()
{
$this->handleSubFun["server/v1"] = ['qos' => 0, 'function' => function ($topic, $msg) {
$msg = json_decode($msg, true);
if($msg){
try {
switch ($msg['event']){
case "event1":
// 消息返回事件类型1
break;
case "event2":
// 消息返回事件类型2
break;
default:
dump('其它消息',$msg);
break;
}
}catch (\Exception $exception){
//代码报错信息
dump($exception->getMessage());
}
}
}];
}
}
三、在app/extend/mqtt/下创建MqttServer.php文件写入
<?php
namespace App\extend\mqtt;
use App\Traits\MqttHandle;
use Bluerhinos\phpMQTT;
/**
* MQTT服务
* Class MqttServer
* @package App\extend\mqtt
*/
class MqttServer extends phpMQTT
{
use MqttHandle;
public function __construct()
{
$config = config('app.mqtt');
$server = $config['server']; // 你的mqtt地址
$port = $config['port']; // 你的mqtt端口
$username = $config['username']; // 账号
$password = $config['password']; // 密码
$client_id = 'phpMQTT_subscriber_' . uniqid(); // 生成连接唯一id
$ca_crt_file = $config['ca_crt']; // mqtt的ssl证书(不使用sll可不写)
parent::__construct($server, $port, $client_id,$ca_crt_file);
// $this->debug = true;
if (!$this->connect(true, NULL, $username, $password)) {
echo '连接失败';
exit(1);
}
}
/**
* 设备开门
* @param $imei
* @param $lock_id string 1左边门 2右边门
*/
public function openDoor($imei,$lock_id){
$this->sendMsg('device/'.$imei,'server.openDoor',['lock_no'=>$lock_id]);
}
/**
* 发送请求
* @param $topic string 设备监听主题
* @param $event string 事件
* @param $extra_params array 参数
*/
public function sendMsg($topic, $event, $extra_params = []){
$arr = explode(' ', microtime());
$client_id = 'server-' . (date('dHis', $arr[1]) . '' . str_replace('0.', '', $arr[0]) . rand(1000, 9999));
$payload = [
'uid' => $client_id,
'msgid' => $event.'_'.uniqid(),
'event' => $event,
'time' => (string)time()
];
$payload = array_merge($payload, $extra_params);
$payload['sign'] = $this->MakeSign($payload);// 根据自己的签名规则修改
$payload = json_encode($payload);
$this->publish($topic,$payload,0,false);
}
/**
* 生成签名
* @param $arr
* @return string
*/
public function makeSign($arr)
{
//签名步骤一:按字典序排序参数
ksort($arr);
$string = $this->toUrlParams($arr);
//签名步骤二:在string后加入KEY
$string = $string . "&key=" . $this->mqtt_key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
return strtoupper($string);
}
public function toUrlParams($arr)
{
$buff = "";
foreach ($arr as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* post请求
* @param $url
* @param $data
* @return bool|mixed|string
*/
public function curlPost($url,$data){
$username = 'admin';
$password = 'public';
$data = json_encode($data);
$ch = curl_init();
//设置post方式提交
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 2);// 异步
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
));
curl_setopt($ch, CURLOPT_USERPWD, "{$username}:{$password}");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$return = curl_exec($ch);
return json_decode($return, true);
}
}
四、在app/Traits下创建MqttHandle.php文件(改写mqtt轮训的debug返回)
<?php
namespace App\Traits;
trait MqttHandle
{
/**
* 开启轮训处理
* @param bool $loop
* @return bool | string
*/
public function proc($loop = true)
{
if (feof($this->socket)) {
$this->_debugMessage('eof receive going to reconnect for good measure');
fclose($this->socket);
$this->connect_auto(false);
if (count($this->topics)) {
$this->subscribe($this->topics);
}
}
$byte = $this->read(1, true);
if ((string)$byte === '') {
if ($loop === true) {
usleep(100000);
}
} else {
$cmd = (int)(ord($byte) / 16);
$this->_debugMessage(
sprintf(
'Received CMD: %d (%s)',
$cmd,
isset(static::$known_commands[$cmd]) === true ? static::$known_commands[$cmd] : 'Unknown'
)
);
$multiplier = 1;
$value = 0;
do {
$digit = ord($this->read(1));
$value += ($digit & 127) * $multiplier;
$multiplier *= 128;
} while (($digit & 128) !== 0);
$this->_debugMessage('Fetching: ' . $value . ' bytes');
$string = $value > 0 ? $this->read($value) : '';
if ($cmd) {
switch ($cmd) {
case 3: //Publish MSG
$return = $this->message($string);
if (is_bool($return) === false) {
return $return;
}
break;
}
}
}
if ($this->timesinceping < (time() - $this->keepalive)) {
$this->_debugMessage('not had something in a while so ping');
$this->ping();
}
if ($this->timesinceping < (time() - ($this->keepalive * 2))) {
$this->_debugMessage('not seen a packet in a while, disconnecting/reconnecting');
fclose($this->socket);
$this->connect_auto(false);
if (count($this->topics)) {
$this->subscribe($this->topics);
}
}
return true;
}
}
五、使用自定义指令测试
php artisan mqtt:sub