• Netty样例之如何使用LengthFieldBasedFrameDecoder


    一  客户端 

    public class NewClient {
    
        private  int port;
        private  String address;
    
        public NewClient(int port,String address) {
            this.port = port;
            this.address = address;
        }
    
        public void start(){
            EventLoopGroup group = new NioEventLoopGroup();
    
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new NewClientChannelInitializer());
    
            try {
                ChannelFuture future = bootstrap.connect(address,port).sync();
                new Thread(new Runnable() {
                    
                    @Override
                    public void run() {
                        while (true) {
                            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                            String message;
                            try {
                                message = br.readLine();
                                Message msg = new Message((byte)0xCA, message.length(), message);
                                future.channel().writeAndFlush(msg);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }finally {
                                try {
                                    br.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                        
                        
                    }
                }).start();
                
                
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) {
            NewClient client = new NewClient(7788,"127.0.0.1");
            client.start();
        }
    }
    public class NewClientChannelInitializer extends ChannelInitializer<SocketChannel> {
    
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
    
            pipeline.addLast(new NewEncoder());
            pipeline.addLast(new NewClientHandler());
        }
    }
    public class NewClientHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            String m = "你好啊,Netty。昂昂";
            Message msg = new Message((byte)0xCA, m.length(), m);
            ctx.writeAndFlush(msg);
        }
    }
    public class NewEncoder extends MessageToByteEncoder<Message> {
    
        @Override
        protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
            if(message == null){
                throw new Exception("未获得消息内容");
            }
    
    
            String msgBody = message.getMsgBody();
            byte[] b = msgBody.getBytes(Charset.forName("utf-8"));
            byteBuf.writeByte(message.getType());
            byteBuf.writeByte(b.length);
            byteBuf.writeBytes(b);
    
    
        }
    }

    二 服务端

    public class NewServer {
        private static final int MAX_FRAME_LENGTH = 1024 * 1024;
        private static final int LENGTH_FIELD_LENGTH = 4;
        private static final int LENGTH_FIELD_OFFSET = 1;
        private static final int LENGTH_ADJUSTMENT = 0;
        private static final int INITIAL_BYTES_TO_STRIP = 0;
    
        private int port;
    
        public NewServer(int port) {
            this.port = port;
        }
    
        public void start(){
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap sbs = new ServerBootstrap()
                        .group(bossGroup,workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .localAddress(new InetSocketAddress(port))
                        .childHandler(new NewServerChannelInitializer(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP))
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true);
    
                ChannelFuture future = sbs.bind(port).sync();
    
                System.out.println("Server start listen at " + port );
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) {
            NewServer server = new NewServer(7788);
            server.start();
        }
    
    }

      

    public class NewServerChannelInitializer extends ChannelInitializer<SocketChannel> {
        private  final int MAX_FRAME_LENGTH;
        private  final int LENGTH_FIELD_LENGTH;
        private  final int LENGTH_FIELD_OFFSET;
        private  final int LENGTH_ADJUSTMENT;
        private  final int INITIAL_BYTES_TO_STRIP;
    
        public NewServerChannelInitializer(int MAX_FRAME_LENGTH, int LENGTH_FIELD_LENGTH, int LENGTH_FIELD_OFFSET, int LENGTH_ADJUSTMENT, int INITIAL_BYTES_TO_STRIP) {
            this.MAX_FRAME_LENGTH = MAX_FRAME_LENGTH;
            this.LENGTH_FIELD_LENGTH = LENGTH_FIELD_LENGTH;
            this.LENGTH_FIELD_OFFSET = LENGTH_FIELD_OFFSET;
            this.LENGTH_ADJUSTMENT = LENGTH_ADJUSTMENT;
            this.INITIAL_BYTES_TO_STRIP = INITIAL_BYTES_TO_STRIP;
        }
    
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
    
            pipeline.addLast(new NewDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP,false));
            // 自己的逻辑Handler
            pipeline.addLast("handler", new NewServerHandler());
        }
    
    }
    public class NewDecoder extends LengthFieldBasedFrameDecoder {
    
        /**
         * 我们在Message类中定义了type和length,这都放在消息头部
         * type占1个字节,length占4个字节所以头部总长度是5个字节
         */
        private static final int HEADER_SIZE = 5;
        private byte type;
        private int length;
        private String msgBody;
    
    
        /**
         *
         * @param maxFrameLength   网络字节序,默认为大端字节序
         * @param lengthFieldOffset 消息中长度字段偏移的字节数
         * @param lengthFieldLength 数据帧的最大长度
         * @param lengthAdjustment 该字段加长度字段等于数据帧的长度
         * @param initialBytesToStrip 从数据帧中跳过的字节数
         * @param failFast 如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException
         */
        public NewDecoder(int maxFrameLength, int lengthFieldOffset,
                          int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip,
                          boolean failFast) {
            super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
                    lengthAdjustment, initialBytesToStrip, failFast);
        }
    
        @Override
        protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
            if(in == null){
                return null;
            }
            if(in.readableBytes() < HEADER_SIZE){
                throw new Exception("错误的消息");
            }
            in = (ByteBuf) super.decode(ctx,in);
            /**
             * 通过源码我们能看到在读的过程中
             * 每读一次读过的字节即被抛弃
             * 即指针会往前跳
             */
            type = in.readByte();
    
            length = in.readByte();
    
    
            if(in.readableBytes() < length){
                throw new Exception("消息不正确");
            }
    
            ByteBuf buf = in.readBytes(length);
            byte[] b = new byte[buf.readableBytes()];
            buf.readBytes(b);
    
            msgBody = new String(b,"UTF-8");
            Message msg = new Message(type,length,msgBody);
            return msg;
        }
    }
    public class NewServerHandler extends SimpleChannelInboundHandler<Object> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
            if(o instanceof Message) {
                Message msg = (Message)o;
                System.out.println("Client->Server:"+channelHandlerContext.channel().remoteAddress()+" send "+msg.getMsgBody());
            }
        }
    }

    三  LengthFieldBasedFrameDecoder介绍

      首先要知道它是一个InBoundHandler,也就是用来把字节转成对象的

      他有四个参数

    public LengthFieldBasedFrameDecoder(
                int maxFrameLength,
                int lengthFieldOffset, int lengthFieldLength,
                int lengthAdjustment, int initialBytesToStrip) 

      maxFrameLength 可以设的稍微大点,比如1M

      lengthFieldOffset 在字节中表示长度的偏移

      lengthFieldLength 表示长度的部分占用几个字节

      lengthAdjustment 如果代表长度字段后立刻跟的是content,此值为0

      initialBytesToStrip 跳过的字节,解码后会把原字节数组进行截断

      源代码中就有注释 其实一般我们都是这么设计的,也够了

    * <pre>
    * <b>lengthFieldOffset</b> = <b>2</b> (= the length of Header 1)
    * <b>lengthFieldLength</b> = <b>3</b>
    * lengthAdjustment = 0
    * initialBytesToStrip = 0
    *
    * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)
    * +----------+----------+----------------+ +----------+----------+----------------+
    * | Header 1 | Length | Actual Content |----->| Header 1 | Length | Actual Content |
    * | 0xCAFE | 0x00000C | "HELLO, WORLD" | | 0xCAFE | 0x00000C | "HELLO, WORLD" |
    * +----------+----------+----------------+ +----------+----------+----------------+

      稍微复杂点的,代表长度字段后不是content

      

    * lengthFieldOffset   = 0
    * lengthFieldLength = 3
    * <b>lengthAdjustment</b> = <b>2</b> (= the length of Header 1)
    * initialBytesToStrip = 0
    *
    * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)
    * +----------+----------+----------------+ +----------+----------+----------------+
    * | Length | Header 1 | Actual Content |----->| Length | Header 1 | Actual Content |
    * | 0x00000C | 0xCAFE | "HELLO, WORLD" | | 0x00000C | 0xCAFE | "HELLO, WORLD" |
    * +----------+----------+----------------+ +----------+----------+----------------+
  • 相关阅读:
    处理数据
    Vue--Vue常用指令及操作
    Vue--Vue实例
    ES6--解构赋值
    ES6--入门
    前端模块化开发
    Node.js--入门
    HTML5--canvas与svg的使用
    input整理
    移动端开发入门
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14334288.html
Copyright © 2020-2023  润新知