Netty自定义protobuf编解码器
Netty使用protobuf作传输,Netty内部已经实现默认的Netty编解码器,在Java服务和客户端之间调用是没问题,如果项目有异构语言的客户端连接Netty服务,使用protobuf作传输,那么Netty的默认编解码器就不适用了。这里自定义了编解码器。这里还有一点问题,自定义的协议头可能存在不同语言大小端的问题,想到的解决办法就是将协议头也作成一个protobuf就能解决了。
协议头
为防止粘包,在protobuff上层添加一层交互协议,每个protobuff包前面加六个字节的协议头定义。其中0-3 表示一个int类型protobuff字节大小,4为保留字段,5为数据类型。
0 1 2 3 4 5
0-8位 9-16位 17-24位 25-32位 保留字段 数据类型
package com.iscas.optocon.distribute.nettyserver.handler;
import com.google.protobuf.MessageLite;
import com.iscas.optocon.protobuf.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import java.util.List;
/**
* //TODO
*
* @author zhuquanwen
* @vesion 1.0
* @date 2018/7/21 21:31
* @since jdk1.8
*/
public class CustomProtobufDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
while (in.readableBytes() > 6) { // 如果可读长度小于包头长度,退出。
in.markReaderIndex();
// 获取包头中的body长度
byte l0 = in.readByte();
byte l1 = in.readByte();
byte l2 = in.readByte();
byte l3 = in.readByte();
int s0 = (int) (l0 & 0xff);
int s1 = (int) (l1 & 0xff);
int s2 = (int) (l2 & 0xff);
int s3 = (int) (l3 & 0xff);
s1 <<= 8;
s2 <<= 16;
s3 <<= 24;
int length = (int) (s0 | s1 | s2 | s3);
// 获取包头中的protobuf类型
in.readByte();
byte dataType = in.readByte();
// 如果可读长度小于body长度,恢复读指针,退出。
if (in.readableBytes() < length) {
in.resetReaderIndex();
return;
}
// 读取body
ByteBuf bodyByteBuf = in.readBytes(length);
byte[] array;
int offset;
int readableLen= bodyByteBuf.readableBytes();
if (bodyByteBuf.hasArray()) {
array = bodyByteBuf.array();
offset = bodyByteBuf.arrayOffset() + bodyByteBuf.readerIndex();
} else {
array = new byte[readableLen];
bodyByteBuf.getBytes(bodyByteBuf.readerIndex(), array, 0, readableLen);
offset = 0;
}
//反序列化
MessageLite result = decodeBody(dataType, array, offset, readableLen);
// ctx.fireChannelRead((Message.MessageBase)result);
out.add(result);
ReferenceCountUtil.release(bodyByteBuf);
}
}
public MessageLite decodeBody(byte dataType, byte[] array, int offset, int length) throws Exception {
if (dataType == 0x00) {
return Message.MessageBase.getDefaultInstance().
getParserForType().parseFrom(array, offset, length);
}
// else if (dataType == 0x01) {
// return OptionTickOuterClass.OptionTick.getDefaultInstance().
// getParserForType().parseFrom(array, offset, length);
// }
return null; // or throw exception
}
}
package com.iscas.optocon.distribute.nettyserver.handler;
import com.google.protobuf.MessageLite;
import com.iscas.optocon.distribute.util.DataUtils;
import com.iscas.optocon.protobuf.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* //TODO
*
* @author zhuquanwen
* @vesion 1.0
* @date 2018/7/21 21:25
* @since jdk1.8
*/
@ChannelHandler.Sharable
public class CustomProtobufEncoder extends MessageToByteEncoder<MessageLite> {
// HangqingEncoder hangqingEncoder;
//
// public CustomProtobufEncoder(HangqingEncoder hangqingEncoder)
// {
// this.hangqingEncoder = hangqingEncoder;
// }
@Override
protected void encode(
ChannelHandlerContext ctx, MessageLite msg, ByteBuf out) throws Exception {
byte[] body = msg.toByteArray();
// byte[] header = encodeHeader(msg, body.length);
byte[] header = DataUtils.encodeHeader(msg, body.length);
out.writeBytes(header);
out.writeBytes(body);
return;
}
}
其他语言想与Netty服务通信,可以使用上面定义的协议头解析。