netty自定义协议解码

来源:互联网 发布:百度炒股软件 编辑:程序博客网 时间:2024/05/21 09:28

继承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粘包的问题。

0 0
原创粉丝点击