Netty为protobuf提供了两个编码器(ProtobufEncoder,ProtobufVarint32LengthFieldPrepender),两个解码器(ProtobufVarint32FrameDecoder,ProtobufDecoder)。
只需要在childHandler()中设置好以上编解码器就可以使用protobuf传输数据。
服务器端:
Netty已经把所有的protobuf的细节给封装过了,包括半包粘包的处理。看一下Netty是如何发送和接受protobuf数据的(基于Netty4.0.33)。
ProtobufVarint32LengthFieldPrepender:
解码的过程无非就是先读出length来,根据length读取出所有的数据来,交给protobuf就能还原消息出来。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());
}
}
}
}