swoole学习之: MQTT (物联网) 服务器

0x00 关于MQTT

简述

MQTTMessage Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT, Internet of Things)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

主要特性

  • 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
  • 对负载内容屏蔽的消息传输。
  • 使用 TCP/IP 提供网络连接。
  • 有三种消息发布服务质量:
    • “至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
    • “至少一次”,确保消息到达,但消息重复可能会发生。
    • “只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
  • 小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。
  • 使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。

消息头部格式

固定头部,使用两个字节,共16位:

0x01 基于swoole的物联网服务器

通过设置 open_mqtt_protocol 选项,启用后会解析 MQTT 包头,Worker 进程的 onReceive 事件每次会返回一个完整的 MQTT 数据包。

$server->set(array(
  'open_mqtt_protocol' => true //启用MQTT包头, 默认是关闭的
));

可以使用 Swoole 作为 MQTT 服务端或客户端,实现一套完整物联网(IOT)解决方案。

完整的 MQTT 协议解析和协程客户端可以使用 simps/mqtt

程序代码 mqtt.php:

//region 简单的协议解析函数库
function decodeValue($data){
    return 256 * ord($data[0]) + ord($data[1]);
}
function decodeString($data){
    $length = decodeValue($data);
    return substr($data, 2, $length);
}
//解析头部数据
function mqttGetHeader($data){
    $byte = ord($data[0]);//固定头部,使用两个字节,共16位:

    $header['type'] = ($byte & 0xF0) >> 4;//第4-7位表示 Message Type
    $header['dup'] = ($byte & 0x08) >> 3;//第3位表示 DUP flag: 打开标志
    $header['qos'] = ($byte & 0x06) >> 1;//第1-2位表示 QoS level: Quality of Service,服务质量
    $header['retain'] = $byte & 0x01;//第0位 RETAIN: 保持

    return $header;
}
function eventConnect($header, $data){
    $connect_info['protocol_name'] = decodeString($data);
    $offset = strlen($connect_info['protocol_name']) + 2;

    $connect_info['version'] = ord(substr($data, $offset, 1));
    $offset += 1;

    $byte = ord($data[$offset]);
    $connect_info['willRetain'] = ($byte & 0x20 == 0x20);
    $connect_info['willQos'] = ($byte & 0x18 >> 3);
    $connect_info['willFlag'] = ($byte & 0x04 == 0x04);
    $connect_info['cleanStart'] = ($byte & 0x02 == 0x02);
    $offset += 1;

    $connect_info['keepalive'] = decodeValue(substr($data, $offset, 2));
    $offset += 2;
    $connect_info['clientId'] = decodeString(substr($data, $offset));
    return $connect_info;
}
// endregion

$server = new Swoole\Server('10.0.2.7', 81, SWOOLE_BASE);
$server->set([
    'open_mqtt_protocol' => true, //启用 MQTT 协议处理
    'worker_num' => 1 //开启一个线程
]);
$server->on('Start', function(){
    echo 'Swoole MQTT server started at '.date('Y-m-d H:i:s').PHP_EOL;//在服务器命令行端显示文字
});
//客户端连接
$server->on('Connect', function($server, $fd){
    echo date('Y-m-d H:i:s').' Client connected fd='.$fd.PHP_EOL;//在服务器命令行端显示文字
});
//接收客户端消息
$server->on('Receive', function($server, $fd, $reactor_id, $data){
    $header = mqttGetHeader($data);//解析头部
    var_dump($header);

    echo 'Received msg from fd='.$fd.', length='.strlen($data).' at '.date('Y-m-d H:i:s').PHP_EOL;

    //固定头部,使用两个字节,共16位:
    if($header['type'] == 1){//Client request to connect to Server
        $response = chr(32).chr(2).chr(0).chr(0);
        eventConnect($header, substr($data, 2));//消息头部占了2个字节, 去掉, 得到消息body
        $server->send($fd, $response);//给客户端发消息
    }elseif($header['type'] == 3){//Publish message
        $offset = 2;
        $topic = decodeString(substr($data, $offset));//得到消息body,并进行解码
        $offset += strlen($topic) + 2;
        $msg = substr($data, $offset);
        echo 'Client fd='.$fd.' msg: '.$topic.PHP_EOL
            .'------------'.PHP_EOL
            .$msg.PHP_EOL;
        //file_put_contents(__DIR__.'/data.log', $fd.':'.PHP_EOL.$data.PHP_EOL, FILE_APPEND);
    }
});
$server->on('Close', function($server, $fd){
    echo 'Client fd='.$fd.' closed at '.date('Y-m-d H:i:s').PHP_EOL;
});
$server->start();

代码部分参考了https://wiki.swoole.com/#/start/start_mqtt, 有改动

demo测试, 暂时还不会, 先TODO标记下吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值