继承ByteToMessageDecoder类复写decode方法,项目中的一段解码规则如下:
1、服务端接收报文分为两种类型,单帧包:head为20字节,多帧包:head为24字节。字节位置:5
2、表示报文体长度字段为2个字节,字节开始位置:18
3、先读取一次buffer(缓存区),查看长度是否大于20,小于20指针回滚不作处理,等待下次读取,如果
大于20,取出帧类型字段判断是否为多帧包,再取出报文体长度,如果为多帧包,则包体长度加4,最后
截取包头加包体的长度报文,返回完整的一个包;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
public class MsgPackDecode extends ByteToMessageDecoder{
private static int FRAMETYPE_OFFSET = 5;//帧类型字节位置
private static int HEAD_LENGHT = 20;//单帧包头部长度 如果为多帧包为24
private static int bodyStartPos = 18;//包体长度标识起始位置
private static int LENGHT_OFFSET = 2;//包体长度标识长度
private static ByteBuf buf = Unpooled.buffer();//创建一个ByteBuf缓存区
private static java.util.concurrent.atomic.AtomicInteger c = new AtomicInteger(1);
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
Object o = decode(ctx, in);
if (o != null) {
out.add(o);
System.err.println(c.getAndIncrement());
}
}
//自定义协议解码
private Object decode(ChannelHandlerContext ctx, ByteBuf in) {
//标记读指针位置,以便可以回滚指针
in.markReaderIndex();
//如果读取的包长度不够head_length,回滚指针不做处理,等待下个包再解析
if(in.readableBytes() < HEAD_LENGHT){
in.resetReaderIndex();
return null;
} else {
//读取包头中帧类型(0:单帧包1:多帧包)信息
in.readBytes(buf, HEAD_LENGHT);
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
//判断帧类型(0:单帧包1:多帧包)
int frameType = (req[FRAMETYPE_OFFSET] & 0x04) == 0x04 ? 1 : 0;//获取帧类型
int bodylenght = byteToint(subBytes(req,bodyStartPos,LENGHT_OFFSET));//获取体长度
if(frameType==0){
//单帧包
// 如发现剩余字节不够包体长度,回滚指针,等待下次解码
if (in.readableBytes() < bodylenght) {
in.resetReaderIndex();
return null;
}
in.readBytes(buf, bodylenght);
ByteBuf frame = ctx.alloc().buffer(HEAD_LENGHT + bodylenght);
frame.writeBytes(buf, 0, HEAD_LENGHT + bodylenght);
buf.clear();
return frame;
}else {
//多帧包
// 如发现剩余字节不够包体长度,回滚指针,等待下次解码
if (in.readableBytes() < bodylenght+4) {
in.resetReaderIndex();
return null;
}
in.readBytes(buf, bodylenght+4);
ByteBuf frame = ctx.alloc().buffer(HEAD_LENGHT + bodylenght+4);
frame.writeBytes(buf, 0, HEAD_LENGHT + bodylenght+4);
buf.clear();
return frame;
}
}
}
/**
* 从一个byte[]数组中截取一部分
* @param src
* @param begin
* @param count
* @return
*/
public static byte[] subBytes(byte[] req, int begin, int count) {
byte[] bs = new byte[count];
for (int i=begin; i<begin+count; i++) bs[i-begin] = req[i];
return bs;
}
/**
* 字节转int
* @param src
* @param begin
* @param count
* @return
*/
public static int byteToint(byte[] res) {
// 一个byte数据左移24位变成0x??000000,再右移8位变成0x00??0000
int targets = (res[0] << 8 & 0xFF00) | (res[1] & 0xFF);
return targets;
}
}
这种方式是为了解决自定义协议tcp粘包的问题。