Netty下Google Protocol Buffer自定義編碼器和解碼器實現

在使用Netty加Google Protocol Buffer進行開發的時候,發現每定義一個實體類的話,對應的服務器的handler就要加上這個類的解碼器,十分的麻煩,因此在自己實現協議的時候,將類型轉化也加了進去。

具體的實現思路是通過繼承ByteToMessageDecoderMessageToMessageEncoder<MessageLiteOrBuilder>這兩個類,通過在數據報文前添加一個字節的標誌位區分類的類型,在解碼的時候通過判斷標識生成對應的類。

具體的代碼則是參照google protocol buffer自帶的netty實現ProtobufEncoderProtobufVarint32FrameDecoder這兩個類寫的。

編碼器實現

/**
 * 自定義的編碼器
 * 數據包的格式
 * 包長度(4B) + 包數據類型(1B)+ 數據
 */
public class CustomEncoder extends MessageToMessageEncoder<MessageLiteOrBuilder> {

    private byte[] encodeHeader(MessageLite msg, int length) {
        byte messageType = 0x0f;//數據類型
        //定義數據的類型
        if (msg instanceof Command.Request) {
            messageType = 0x00;
        } else if (msg instanceof Command.Response) {
            messageType = 0x01;
        } else if (msg instanceof Command.Data) {
            messageType = 0x02;
        }
        //數據包頭
        byte[] header = new byte[5];
        //數據長度
        int i = 0;
        //將int32的4個字節提取到數組
        for (byte b : ByteUtil.int2Bytes(length)) {
            header[i++] = b;
        }
        //數據類型
        header[4] = messageType;
        return header;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List<Object> out) throws Exception {
        byte[] body;
        byte[] header;
        if (msg instanceof MessageLite) {
            body = ((MessageLite) msg).toByteArray();
            header = encodeHeader(((MessageLite) msg), body.length);
            //寫數據
            out.add(wrappedBuffer(ByteUtil.concat(header, body)));
        } else if (msg instanceof MessageLite.Builder) {
            body = ((MessageLite.Builder) msg).build().toByteArray();
            header = encodeHeader(((MessageLite.Builder) msg).build(), body.length);
            out.add(wrappedBuffer(ByteUtil.concat(header, body)));
        }
    }
}

解碼器實現

public class CustomDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws InvalidProtocolBufferException {
        if (in.isReadable(5)) {
            //可以查看源碼裏面類的註釋
            //文件操作的讀寫指針是分開的
            in.markReaderIndex();
            //長度
            int length = 0;
            for (int ix = 0; ix < 4; ++ix) {
                length <<= 8;
                length |= (in.readByte() & 0xff);
            }
            //類型
            byte type = in.readByte();

            //數據長度不夠
            if (in.readableBytes() < length) {
                in.resetReaderIndex();
                return;
            }

            ByteBuf body = in.readBytes(length);

            byte[] array;
            int offset;

            int readableLen = body.readableBytes();
            //判斷ByteBuf是否有支撐數組,如果沒有則說明其是用的是直接緩存模式
            if (body.hasArray()) {
                array = body.array();
                offset = body.arrayOffset() + body.readerIndex();
            } else {
                //需要生成一個數組來保存
                array = new byte[readableLen];
                body.getBytes(body.readerIndex(), array, 0, readableLen);
                offset = 0;
            }


            switch (type) {
                case 0x00:
                    out.add(Command.Request.getDefaultInstance()
                            .getParserForType().parseFrom(array, offset, length));
                    break;
                case 0x01:
                    out.add(Command.Response.getDefaultInstance()
                            .getParserForType().parseFrom(array, offset, length));
                    break;
                case 0x02:
                    out.add(Command.Data.getDefaultInstance()
                            .getParserForType().parseFrom(array, offset, length));
                    break;
            }
        }

    }

}

最後只要添加進handler即可

 .childHandler(new ChannelInitializer<SocketChannel>() {
     @Override
     public void initChannel(SocketChannel ch) {
         ChannelPipeline pipeline = ch.pipeline();
         pipeline.addLast("decoder", new CustomDecoder());
         pipeline.addLast("encoder", new CustomEncoder());
     }
 })

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章