thinkphp5 开发mqtt

thinkphp5 开发mqtt

1,这里要安装swoole,创建TP5项目redis环境,请自行安装

2,可先了解mqtt的协议,相关文档为:mqtt

3,创建好TP5项目后

请在根目录composer require simps/mqtt,安装好simps/mqtt后,在TP5项目public文件下,加入我写的mqtt服务端文件代码,文件名随便定义,运行方法:php mqtt_server.php,代码写得差,不爱请别伤害

<?php

try {

    require_once __DIR__ . '/../thinkphp/base.php';
    \think\Container::get('app')->path(__DIR__ . '/../application/')->initialize();//应用初始化,这里是为了我的redis文件调用一些函数

    $server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS);

    $server->set([
        'open_mqtt_protocol' => true, // 启用 MQTT 协议
        'worker_num' => 2,
        'reactor_num' => 4,
        'reload_async' => true,//设置异步重启开关。设置为 true 时,将启用异步安全重启特性,Worker 进程会等待异步事件完成后再退出
        'dispatch_mode' => 2,//数据包分发策略。【默认值:2】
        //模式值	模式	作用
        //1	轮循模式	收到会轮循分配给每一个 Worker 进程
        //2	固定模式	根据连接的文件描述符分配 Worker。这样可以保证同一个连接发来的数据只会被同一个 Worker 处理
        //3	抢占模式	主进程会根据 Worker 的忙闲状态选择投递,只会投递给处于闲置状态的 Worker
        //4	IP 分配	根据客户端 IP 进行取模 hash,分配给一个固定的 Worker 进程。
        //可以保证同一个来源 IP 的连接数据总会被分配到同一个 Worker 进程。算法为 ip2long(ClientIP) % worker_num
        //5	UID 分配	需要用户代码中调用 Server->bind() 将一个连接绑定 1 个 uid。然后底层根据 UID 的值分配到不同的 Worker 进程。
        //算法为 UID % worker_num,如果需要使用字符串作为 UID,可以使用 crc32(UID_STRING)
        //7	stream 模式	空闲的 Worker 会 accept 连接,并接受 Reactor 的新请求
        'daemonize' => false,//是否后台运行
//    'open_tcp_keepalive' => true,//在 TCP 中有一个 Keep-Alive 的机制可以检测死连接
//    'tcp_keepidle' => 4,//4s没有数据传输就进行检测
//    'tcp_keepinterval' => 1,//1s探测一次
//    'tcp_keepcount' => 5,//探测的次数,超过5次后还没回包close此连接
//    'heartbeat_check_interval' => 60,//启用心跳检测【默认值:false】
//    'heartbeat_idle_time' => '600',//连接最大允许空闲的时间
//    'package_max_length' => 2 * 1024 * 1024,//默认2m
        'open_cpu_affinity' => true,//启用 CPU 亲和性设置
//    'buffer_input_size' => 2 * 1024 * 1024,//配置接收输入缓存区内存尺寸。【默认值:2M】
//    'buffer_output_size' => 2 * 1024 * 1024,//配置发送输出缓存区内存尺寸
//    'socket_buffer_size' => 2 * 1024 * 1024,//配置客户端连接的缓存区长度。【默认值:2M】
    ]);

    $server->on('Connect', function ($server, $fd) {
        echo "Client:Connect.\n";
    });

    $server->on('Receive', function ($server, $fd, $reactor_id, $data) {
        try {
            $arr = \Simps\MQTT\Protocol\V3::unpack($data);//客户端连接信息
            if (is_array($arr) && isset($arr['type'])) {
                switch ($arr['type']) {
                    case \Simps\MQTT\Protocol\Types::CONNECT://客户端连接
                        //用户名密码连接
                        if (!isset($arr['user_name']) || empty($arr['user_name']) || !isset($arr['password']) || empty($arr['password'])) {
                            $server->close($fd);
                            return;//用户密码为空
                        }
                        //判断连接

                        //遗嘱
                        if (isset($arr['will']['topic']) && !empty($arr['will']['topic'])) {
                            $redis = new \app\common\model\Redis100_model();
                            $redis->hSet('will', $fd, json_encode(['message' => $arr['will']['message'], 'qos' => $arr['will']['qos'], 'topic' => $arr['will']['topic'], 'retain' => $arr['will']['retain']]));
                        }
                        $server->send($fd, \Simps\MQTT\Protocol\V3::pack(['type' => \Simps\MQTT\Protocol\Types::CONNACK, 'code' => 0, 'session_present' => 0]));//确认连接请求
                        break;
                    case \Simps\MQTT\Protocol\Types::PUBLISH://发布,接收信息
                        $redis = new \app\common\model\Redis100_model();
                        foreach ($server->connections as $sub_fd) {
                            $publish = $redis->sIsMember($sub_fd, $arr['topic']);
                            if ($publish !== false) {
                                //发送所有相同的订阅主题
                                $server->send($sub_fd, \Simps\MQTT\Protocol\V3::pack(
                                    [
                                        'type' => $arr['type'],
                                        'topic' => $arr['topic'],
                                        'message' => $arr['message'],
                                        'dup' => $arr['dup'],
                                        'qos' => $arr['qos'],
                                        'retain' => $arr['retain'],
                                        'message_id' => $arr['message_id'] ?? 0,
                                    ]
                                ));
                            }
                        }
                        if ($arr['qos'] === 1) {//发布消息的服务质量,即:保证消息传递的次数
                            $server->send($fd, \Simps\MQTT\Protocol\V3::pack(
                                [
                                    'type' => \Simps\MQTT\Protocol\Types::PUBACK,
                                    'message_id' => $arr['message_id'] ?? 0,
                                ]
                            ));
                        }
                        $redis->close();
//                    $offset = 2;
//                    $topic = decodeString(substr($arr, $offset));//订阅主题
//                    $offset += strlen($topic) + 2;
//                    $msg = substr($arr, $offset);//信息
//                    $server->send($fd, $arr);
                        break;
                    case \Simps\MQTT\Protocol\Types::SUBSCRIBE://订阅
                        $payload = [];
                        $redis = new \app\common\model\Redis100_model();
                        $redis->del($fd);//防止旧数据
                        foreach ($arr['topics'] as $k => $qos) {
                            $redis->sAdd($fd, $k);
                            if (is_numeric($qos) && $qos < 3) {
                                $payload[] = $qos;
                            } else {
                                $payload[] = 0x80;
                            }
                        }
                        $redis->close();
                        $server->send($fd, \Simps\MQTT\Protocol\V3::pack(['type' => \Simps\MQTT\Protocol\Types::SUBACK, 'message_id' => $arr['message_id'] ?? 0, 'codes' => $payload]));
                        break;
                    case \Simps\MQTT\Protocol\Types::PINGREQ://ping心跳
                        if ($server->exist($fd)) {
                            $server->send($fd, \Simps\MQTT\Protocol\V3::pack(['type' => \Simps\MQTT\Protocol\Types::PINGRESP]));
                        }
                        break;
                    case \Simps\MQTT\Protocol\Types::DISCONNECT:
                        if ($server->exist($fd)) {
                            $redis = new \app\common\model\Redis100_model();
                            //转发遗嘱
                            $will = $redis->hGet('will', $fd);
                            if (!empty($will)) {
                                $will = json_decode($will, true);
                                foreach ($server->connections as $sub_fd) {
                                    if ($sub_fd != $fd) {
                                        //发送所有相同的订阅主题
                                        $server->send($sub_fd, \Simps\MQTT\Protocol\V3::pack(
                                            [
                                                'type' => 3,
                                                'topic' => $will['topic'],
                                                'message' => $will['message'],
                                                'dup' => 0,
                                                'qos' => $will['qos'],
                                                'retain' => $will['retain'],
                                                'message_id' => $arr['message_id'] ?? 0,
                                            ]
                                        ));
                                    }
                                }
                                if ($will['qos'] === 1) {//发布消息的服务质量,即:保证消息传递的次数

                                }
                            }
                            $redis->hdel('will', $fd);
                            $redis->del($fd);
                            $redis->close();
                            $server->close($fd);
                        }
                        break;
                    case \Simps\MQTT\Protocol\Types::UNSUBSCRIBE:
                        $server->send($fd, \Simps\MQTT\Protocol\V3::pack(
                            [
                                'type' => \Simps\MQTT\Protocol\Types::UNSUBACK,
                                'message_id' => $arr['message_id'] ?? 0,
                            ]
                        ));
                        $redis = new \app\common\model\Redis100_model();
                        $redis->del($fd);
                        $redis->close();
                        break;
                }
            }
        } catch (\Exception $e) {
            var_dump($e->getMessage());
        } catch (\Throwable $e) {
            var_dump($e->getMessage());
        }
    });

    $server->on('Shutdown', function ($server) {
        echo "Shutdown\n";
    });

    $server->on('WorkerStop', function ($server, $workerId) {
        echo "WorkerStop\n";
    });

    $server->on('WorkerExit', function ($server, $workerId) {
        echo "WorkerExit\n";
    });

    $server->on('WorkerError', function ($server, $workerId, $worker_pid, $exit_code, $signal) {
        echo "WorkerError\n";
    });

    $server->on('ManagerStop', function ($server) {
        echo "ManagerStop\n";
    });

    $server->on('Close', function ($server, $fd) {
        $redis = new \app\common\model\Redis100_model();
        $redis->hdel('will', $fd);
        $redis->del($fd);
        $redis->close();
        echo "Client: Close.\n";
    });

    function decodeString($arr)
    {
        $length = 256 * ord($arr[0]) + ord($arr[1]);
        return substr($arr, 2, $length);
    }

    $server->start();
} catch (\Exception $e) {
    var_dump($e->getMessage());
} catch (\Throwable $e) {
    var_dump($e->getMessage());
}

4,当然你觉得这代码不行,也可以根据文档来改(有客户端代码),请点击查看simps/mqtt文档

喜欢的靓仔靓妹们,点个赞吧

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你有多高, 六尺七寸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值