netty protobuf编码分析

proto文件定义

message packageV1 {
    int32 id = 1;
    string name = 2;
    Type type = 3;

    enum Type {
        DEFAULT = 0;
        LOGIN = 1;
        SEND = 2;
        TO_ALL = 3;
    }
}

java创建一个pb对象

PackageV1.packageV1.Builder builder = PackageV1.packageV1.newBuilder();
        builder.setId(1);
        builder.setName(RandomUtil.randomString(128));//name内容是128英文字母
        builder.setType(PackageV1.packageV1.Type.TO_ALL);
        PackageV1.packageV1 build = builder.build();
        int length = build.toByteArray().length;
        log.info("package {} size[{}]",build,length);
        return build;

toStrng输出

 package id: 1
name: "b2S(C+m+6XAXySdv7D8)qErdAvr~#VrA0j*8NoOm5d5f52xm6$lW5Nd~EBGVFjkdD7UUsULCxgAd&ZVrydMlq3wYOzs~Gjyh+p~ZIi&qO+owwQ$p2)m&+Ss!DnQ#cFs~"
type: TO_ALL
 size[135]

也就是说整个pb包的大小是135个字节,其16进制的内容是

080112800129486862316A36256628344F4C594B6232712677294C41584468716577293323265A24422838464B6D7141266D49255A7E36614D5962457223515F67364B36295F434F6D2135294E69714F6369767943364F2644366F65486A76467E325E7233653321744D7A47732633724D6F6A756A6A73235F46425A3249716E75582171301803

而在网络中传输(wireshare抓包或其它网络调式工具观察)的实际字节内容

87 01 08 01 12 80 01 29 48 68 62 31 6A 36 25 66 28 34 4F 4C 59 4B 62 32 71 26 77 29 4C 41 58 44 68 71 65 77 29 33 23 26 5A 24 42 28 38 46 4B 6D 71 41 26 6D 49 25 5A 7E 36 61 4D 59 62 45 72 23 51 5F 67 36 4B 36 29 5F 43 4F 6D 21 35 29 4E 69 71 4F 63 69 76 79 43 36 4F 26 44 36 6F 65 48 6A 76 46 7E 32 5E 72 33 65 33 21 74 4D 7A 47 73 26 33 72 4D 6F 6A 75 6A 6A 73 23 5F 46 42 5A 32 49 71 6E 75 58 21 71 30 18 03

仔细对比可以发现多了87 01 两个字节的内容,这两个字节的内容是什么呢?在netty中使用protobuf编解码的过程中,模板代码如下

 ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
            ch.pipeline().addLast(new ProtobufDecoder(PackageV1.packageV1.getDefaultInstance()));
            ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
            ch.pipeline().addLast(new ProtobufEncoder());
            ch.pipeline().addLast(new IoEventHandler(snowflakeIdWorker));

实际上就是ProtobufVarint32LengthFieldPrependerr的encode方法将整个pb包的二进制 包的大小写在了整个包字节的前面[length] |[data]这种形式,
但是87 01这两个字节是怎么得出135的呢,其实是pb中的变长编码

HEX 87 01 //16进制
BIN 10000111  00000001   //2进制

依据pb变长编码规则

00000001  0000111=128+4+2+1=135

所以在解码的时候需要先读出这个长度值后读取再后续字节才能decode出一个PB包
这个头长度的读取源码见io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder#readRawVarint32
详细代码

//在网络传输的过程中低位在左边
if (!buffer.isReadable()) {
            return 0;
        }
        buffer.markReaderIndex();
        byte tmp = buffer.readByte();//读一个字节
        if (tmp >= 0) {//如果是正数,说明这个长度的值读取完了,直接返回,(0,127]
            return tmp;
        } else {//负数,则是不完整的
            int result = tmp & 127;//丢字节的msb位
 //如果此时没有可度的内容,说明表示长度的数据没有完整发送,重置读指针
            if (!buffer.isReadable()) {
                buffer.resetReaderIndex();
                return 0;
            }
            //没有后续字节了,按变长数字转化,两个字节的编码
            if ((tmp = buffer.readByte()) >= 0) {
                result |= tmp << 7;  
            } else {
                result |= (tmp & 127) << 7;
                if (!buffer.isReadable()) {
                    buffer.resetReaderIndex();
                    return 0;
                }
                //三个字节编码
                if ((tmp = buffer.readByte()) >= 0) {
                    result |= tmp << 14;
                } else {
                    result |= (tmp & 127) << 14;
                    if (!buffer.isReadable()) {
                        buffer.resetReaderIndex();
                        return 0;
                    }
                    //四个字节编码
                    if ((tmp = buffer.readByte()) >= 0) {
                        result |= tmp << 21;
                    } else {
                        result |= (tmp & 127) << 21;
                        if (!buffer.isReadable()) {
                            buffer.resetReaderIndex();
                            return 0;
                        }
                        //五个字节编码
                        result |= (tmp = buffer.readByte()) << 28;
                        if (tmp < 0) {
                            throw new CorruptedFrameException("malformed varint.");
                        }
                    }
                }
            }
            return result;
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值