分隔符和定长解码器在netty中的应用

两种解码器:DelimiterBasedFrameDecoder和FixedLengthFrameDecoder
前者可以自动完成以分隔符做结束标志的消息的解码;

后者可以自动完成对定长消息的解码。

首先是分隔符解码器在netty中的应用,服务端:

public class EchoServer {

    public static void main(String[] args) {
        int port = 6767;
        if(args!=null&&args.length>0){
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){
                //采用默认值
            }
        }
        new EchoServer().blind(port);
    }

    public void blind(int port){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //本例中使用"$_"作为分隔符
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
                            /**
                             * 创建DelimiterBasedFrameDecoder对象,它有多个构造方法;
                             * 1024代表单条消息的最大长度,当达到该长度后任然没有找到分隔符,
                             * 就抛出ToolLongFrameException异常;
                             * 第二个参数就是分隔符缓冲对象
                             */
                            ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));
                            //将ByteBuf解码成字符串对象
                            ch.pipeline().addLast(new StringDecoder());
                            //EchoServerHandler接收到的msg消息就是解码后的字符串对象
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
EchoServerHandler类:

public class EchoServerHandler extends ChannelHandlerAdapter{
    int counter = 0;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        /**
         * DelimiterBasedFrameDecoder自动请求消息进行了解码
         * 后续的ChannelHandler接收到的msg对象就是个完整的消息包
         */
        String body = (String) msg;
        System.out.println("This is "+ ++counter+"times receive client :【"+
                    body+"】");
        /**
         * 由于DelimiterBasedFrameDecoder过滤掉了分隔符,
         * 所以返回给客户端是需要在请求消息尾部拼接分隔符"$_"
         */
        body += "$_";
        ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
        ctx.writeAndFlush(echo);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
客户端代码:

public class EchoClient {

    public static void main(String[] args) {
        int port = 6767;
        if(args!=null&&args.length>0){
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){
                //采用默认值
            }
        }
        new EchoClient().connect(port,"127.0.0.1");
    }

    public void connect(int port,String host){
        //配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
                            ch.pipeline().addLast(
                                    new DelimiterBasedFrameDecoder(1024,delimiter)
                            );
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //发起异步连接操作
            ChannelFuture f = b.connect(host,port).sync();
            //等待客户端链路关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();//优雅退出,释放NIO线程组
        }
    }
}

EchoClientHandler类:

public class EchoClientHandler extends ChannelHandlerAdapter{
    private int counter;

    static final String ECHO_REQ = "Hi,LING. Welcome to Netty.$_";

    public EchoClientHandler(){}

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i = 0;i<10;i++){
            ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("This is "+ ++counter+"time receive server:["+msg+"]");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

分别启动服务端和客户端:服务端运行结果如下:

客户端运行结果:



即客户端发送了10条信息到服务端,并且服务端也接收到了客户端的10条信息,本例通过“$_”分割符,

用DelimiterBasedFrameDecoder对消息进行分割,没有出现粘包和拆包的现象。


定长解码器FixedLengthFrameDecoder在Netty中的应用:

服务端开发:

public class EchoServer1 {

    public static void main(String[] args) {
        int port = 6765;
        if(args!=null&&args.length>0){
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){
                //采用默认值
            }
        }
        new EchoServer1().blind(port);
    }

    public void blind(int port){
        //配置服务端的NIO线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new FixedLengthFrameDecoder(20)); //定长解码器,截取20个字节长度的请求消息
                            p.addLast(new StringDecoder());
                            p.addLast(new EchoServerHandler1());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
EchoServerHandler1类:

public class EchoServerHandler1 extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Receive client :["+msg+"]");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();//发生异常,关闭链路
    }
}

启动服务端,利用telnet命令进行测试EchoServer1服务端:

1.在【运行】菜单中输入cmd命令,打开命令行窗口;

2.在命令行输入“telnet localhost 6765”连接服务端;(和服务端端口对应) 

  

3.输入命令行内容(需要开启本地回显才能看到输入的内容,我这里一直输入aaaa)

服务端运行结果:


由于之前设置了截取20字节长度,所以这里有20个a

这就是FixedLengthFrameDecoder在netty中的应用。


你可能感兴趣的:(Netty,高性能网络通信框架—Netty)