前面的话
直接进入主题,具体的mqtt协议请参考mqtt协议
固定头的结构
简单描述下固定头
-
第一个字节总共8位, 前四位表示消息类型,最大的十进制值是15(二进制是1111)。
-
第三位表示消息是否重复,十进制值只能保存0或者1
-
第一、二位是消息的QoS值,十进制值能存0(二进制00)到3(二进制11)的任意值
-
最后一位是服务器是否需要保存此消息的标识,十进制值存0或者1
-
第二个字节标识 除了固定头部之外的 消息的 长度(可变头+消息体),这里先不讨论
_ 更具体的,请看mqtt协议。_
编码
// 先定义一个变量,值为0, 一个数字占用一个字节。
// 所以 firstByte 的二进制值是 0000 0000
// 注:有些实现使用的是16进制,使用16进制和10进制没啥区别,
// 只是16进制转换为2进制方便些,为了好理解,这里全部用10进制
$firstByte = 0;
// 假设Message Type 是5,所以构造出来的头部
// 第一字节前四位应该是 0101(5的二进制),其他位都为0的时候
// 第一字节二进制是 0101 0000
$firstByte |= 5; // 原值 0000 0000 和 0101(可以看作是:0000 0101) 做或运算,得到的值是 0000 0101
// 需要将 0000 0101 变成 0101 0000 只需要左移4位
$firstByte << 4; // $firstByte 二进制是 0101 0000 ,至此 message type 就编到了第一自己的前四位中了。
// 接下来是第三位,dup 的标识,假设我们的dug flag 值是1,我们需要将第三位设置为1
// 将firstByte 从 0101 0000 变成 0101 1000,只需要 0101 0000 和 0000 1000 做一次 或运算即可
// 0000 1000的十进制值是 8
$firstByte |= 8; // firstByte 的二进制是 0101 1000,至此 dup flag 就编到了第三位中
// 接下来是第一、二位,用存储qos level的值
// 假设qos level 的值是3(这里只是假设,3的是mqtt预留字段),3的二进制是 11
// 那么放入存储3以后,firstByte 的二进制值应该是 0101 1110
// 我们只需要将 0101 1000 和 0000 0110 作或运算即可,0000 0110 的十进制是 6
$firstByte |= 6; // firstByte 的二进制是 0101 1110
// 如何编码 retain, 相信,你已经会了
// 最后一步,我们需要将上面得到的结果转换成一个字符串
$firstChr = chr($firstByte)
// 然后发送出去
解析
//在上一步中,假设我们把 retain = 1 也编进了第一字节,那会得到 一个二进制 0101 1111。
// 通过 chr 转换为一个字符串后发出来的,我们需要还原。假设$buffer 是收到的数据.
$firstChr = substr($buffer, 0, 1); // 取第一个字节
$firstByte = ord($firstChr); // 转换回asiic码,这个时候我们就得到了一个二进制为 0101 1111的变量
// 首先取前四位,也就是message type, 只需要右移 4 位即可
$messageType = $firstByte >> 4; // 0101 1111 >> 4 = 0000 0101,值为十进制的5
// 现在要取第三位 dug flag
$dupFlag = ($firstByte & 8) >> 3; // 0101 1111 & 000 1000(8 的二进制) = 000 1000 >> 3 = 0000 0001 = 十进制1
// 取 qos level
$qos = ($firstByte & 6) >> 1; // 0101 1111 & 0000 0110(6 的二进制) = 0000 0110 >> 1 = 0000 0011 = 十进制3
// 最后一位怎么取, firstByte 和 1 作与运算即可。
结语
首先,位运算是一些很有技巧性,上面只是一种方式,你还可以通过其他的位运算方式实现
其次,不要工作几年就忘记了基础,[捂脸]