在使用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());
}
})