在使用Netty加Google Protocol Buffer进行开发的时候,发现每定义一个实体类的话,对应的服务器的handler就要加上这个类的解码器,十分的麻烦,因此在自己实现协议的时候,将类型转化也加了进去。
具体的实现思路是通过继承ByteToMessageDecoder
和MessageToMessageEncoder<MessageLiteOrBuilder>
这两个类,通过在数据报文前添加一个字节的标志位区分类的类型,在解码的时候通过判断标识生成对应的类。
具体的代码则是参照google protocol buffer自带的netty实现ProtobufEncoder
和ProtobufVarint32FrameDecoder
这两个类写的。
编码器实现
/**
* 自定义的编码器
* 数据包的格式
* 包长度(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());
}
})