Netty protobuf的编解码使用

来源:互联网 发布:淘宝宝贝仓库在哪 编辑:程序博客网 时间:2024/05/21 15:02
Netty为protobuf提供了两个编码器(ProtobufEncoder,ProtobufVarint32LengthFieldPrepender),两个解码器(ProtobufVarint32FrameDecoder,ProtobufDecoder)。

只需要在childHandler()中设置好以上编解码器就可以使用protobuf传输数据。
服务器端:
.childHandler(new ChannelInitializer<SocketChannel>() {    @Override    protected void initChannel(SocketChannel ch) throws Exception {        ch.pipeline()                .addLast(new ProtobufVarint32FrameDecoder())                .addLast(new ProtobufDecoder(                        ProtoObject.Req.getDefaultInstance()))                                .addLast(new ProtobufVarint32LengthFieldPrepender())                .addLast(new ProtobufEncoder())                                .addLast(new ServerHandler());    }})
客户端:
.childHandler(new ChannelInitializer<SocketChannel>() {    @Override    protected void initChannel(SocketChannel ch) throws Exception {        ch.pipeline()                .addLast(new ProtobufVarint32FrameDecoder())                .addLast(new ProtobufDecoder(                        ProtoObject.Resp.getDefaultInstance()))                                .addLast(new ProtobufVarint32LengthFieldPrepender())                .addLast(new ProtobufEncoder())                                .addLast(new ClientHandler());    }})

Netty已经把所有的protobuf的细节给封装过了,包括半包粘包的处理。看一下Netty是如何发送和接受protobuf数据的(基于Netty4.0.33)。
public class ProtobufEncoder extends MessageToMessageEncoder<MessageLiteOrBuilder> {    @Override    protected void encode(            ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List<Object> out) throws Exception {        if (msg instanceof MessageLite) {            out.add(wrappedBuffer(((MessageLite) msg).toByteArray()));            return;        }        if (msg instanceof MessageLite.Builder) {            out.add(wrappedBuffer(((MessageLite.Builder) msg).build().toByteArray()));        }    }}
encode方法很简单,实际上它会调用protobuf的api,把消息编码成protobuf格式的字节数组。

ProtobufVarint32LengthFieldPrepender:
public class ProtobufVarint32LengthFieldPrepender extends MessageToByteEncoder<ByteBuf> {    @Override    protected void encode(            ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {        int bodyLen = msg.readableBytes();        int headerLen = CodedOutputStream.computeRawVarint32Size(bodyLen);        out.ensureWritable(headerLen + bodyLen);        CodedOutputStream headerOut =                CodedOutputStream.newInstance(new ByteBufOutputStream(out), headerLen);        headerOut.writeRawVarint32(bodyLen);        headerOut.flush();        out.writeBytes(msg, msg.readerIndex(), bodyLen);    }}
它会在原来的数据的前面,追加一个使用Base 128 Varints编码过的length。例如:
* BEFORE DECODE (300 bytes)       AFTER DECODE (302 bytes)* +---------------+               +--------+---------------+* | Protobuf Data |-------------->| Length | Protobuf Data |* |  (300 bytes)  |               | 0xAC02 |  (300 bytes)  |* +---------------+               +--------+---------------+

解码的过程无非就是先读出length来,根据length读取出所有的数据来,交给protobuf就能还原消息出来。ProtobufVarint32FrameDecoder:
public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {    // TODO maxFrameLength + safe skip + fail-fast option    //      (just like LengthFieldBasedFrameDecoder)    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {        in.markReaderIndex();        final byte[] buf = new byte[5];        for (int i = 0; i < buf.length; i ++) {            if (!in.isReadable()) {                in.resetReaderIndex();                return;            }            buf[i] = in.readByte();            if (buf[i] >= 0) {                int length = CodedInputStream.newInstance(buf, 0, i + 1).readRawVarint32();                if (length < 0) {                    throw new CorruptedFrameException("negative length: " + length);                }                if (in.readableBytes() < length) {                    in.resetReaderIndex();                    return;                } else {                    out.add(in.readBytes(length));                    return;                }            }        }        // Couldn't find the byte whose MSB is off.        throw new CorruptedFrameException("length wider than 32-bit");    }}

ProtobufDecoder:
public class ProtobufDecoder extends MessageToMessageDecoder<ByteBuf> {    private static final boolean HAS_PARSER;    static {        boolean hasParser = false;        try {            // MessageLite.getParsetForType() is not available until protobuf 2.5.0.            MessageLite.class.getDeclaredMethod("getParserForType");            hasParser = true;        } catch (Throwable t) {            // Ignore        }        HAS_PARSER = hasParser;    }    private final MessageLite prototype;    private final ExtensionRegistryLite extensionRegistry;    /**     * Creates a new instance.     */    public ProtobufDecoder(MessageLite prototype) {        this(prototype, null);    }    public ProtobufDecoder(MessageLite prototype, ExtensionRegistry extensionRegistry) {        this(prototype, (ExtensionRegistryLite) extensionRegistry);    }    public ProtobufDecoder(MessageLite prototype, ExtensionRegistryLite extensionRegistry) {        if (prototype == null) {            throw new NullPointerException("prototype");        }        this.prototype = prototype.getDefaultInstanceForType();        this.extensionRegistry = extensionRegistry;    }    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {        final byte[] array;        final int offset;        final int length = msg.readableBytes();        if (msg.hasArray()) {            array = msg.array();            offset = msg.arrayOffset() + msg.readerIndex();        } else {            array = new byte[length];            msg.getBytes(msg.readerIndex(), array, 0, length);            offset = 0;        }        if (extensionRegistry == null) {            if (HAS_PARSER) {                out.add(prototype.getParserForType().parseFrom(array, offset, length));            } else {                out.add(prototype.newBuilderForType().mergeFrom(array, offset, length).build());            }        } else {            if (HAS_PARSER) {                out.add(prototype.getParserForType().parseFrom(array, offset, length, extensionRegistry));            } else {                out.add(prototype.newBuilderForType().mergeFrom(array, offset, length, extensionRegistry).build());            }        }    }}



原创粉丝点击