mqtt协议剩余长度字段编解码实现方案(java)

508 阅读2分钟

  根据mqtt协议的定义,剩余长度字段是可变的,最少用一个字节最多四个字节来表示,而且每个字节的最高位不表示有效数据,用来表示有没有下一个字节。所以每个字节能表示的数据的最大值是128,所以剩余长度字段可表示的最大值是128 * 128 * 128 * 128,则理论上能表示数据包大小的最大值是256M。

  对剩余长度字段的编解码,可理解为128进制的运算。如果低位字节满128,则向高位字节进1。那么进位系数就是128。

  下面是用一段代码描述对剩余长度字段的计算方法(java实现)。


    public static void main(String[] args) throws Exception{
        int length = 300;
        final byte[] encode = encode(length);
        System.out.println("编码:"+length);
        for (byte b : encode) {
            System.out.println(getBinaryStrFromByte(b));
        }
        System.out.println();
        System.out.println("解码:"+decode(encode));
    }

    /**
     * mqtt固定报文头剩余长度的编码方案
     * @param x 字节大小
     * @return 编码结果
     */
    public static byte[] encode(int x){
        byte encodeBytes[] = new byte[x < 2<<6 ? 1 : x < 2<<13 ? 2 : x < 2<<20 ? 3 : 4];
        int encodeByte;
        int i=0;
        do{
            // 128进制,满128进1
            encodeByte = x % 128;
            x = x / 128;
            if( x > 0 )
                // encodeByte | 128 保证最高位必须是1
                encodeByte = encodeByte | 128;
            encodeBytes[i]=(byte)encodeByte;
            i+=1;
        }while (x>0);
        return encodeBytes;
    }

    /**
     * mqtt固定报文头剩余长度的编码方案
     * @param bytes 编码后的字节数据
     * @return 解码后的字节剩余长度
     */
    public static int decode(byte[] bytes){
        // multiplier 进位系数,第一个字节的进位系数是1,第二个字节的进位系数是128,
        // 第三个字节的进位系数是 128 * 128,第四个字节的进位系数是 128 * 128 * 128
        int multiplier = 1;
        int value = 0;
        int i=0;
        byte encodedByte;
        do {
            encodedByte = bytes[i];
            // encodedByte & 127 去掉最高位的1,因为最高位不表示数据
            value += (encodedByte & 127) * multiplier;
            multiplier *= 128;
            if (multiplier > 128 * 128 * 128)
                return -1;
            i+=1;
            // (encodedByte & 128) != 0 代表encodedByte该字节的最高位是1,
            // 所以根据mqtt对剩余字段长度的定义应该还有下一个字节。
        }while ((encodedByte & 128) != 0);
        return value;
    }

    /**
     * 字节转二进制字符串
     * @param encodeByte byte
     * @return 二进制字符串
     */
    public static String getBinaryStrFromByte(byte encodeByte){
        StringBuilder result = new StringBuilder();
        byte a = encodeByte; ;
        for (int i = 0; i < 8; i++){
            int i1 = a % 2;
            result.append(i1>0?i1:-i1);
            a=(byte)(a>>1);
        }
        return result.reverse().toString();
    }