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

来源:互联网 发布:正规淘宝刷客平台 编辑:程序博客网 时间:2024/04/30 02:19
两种解码器: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<SocketChannel>() {                        @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<SocketChannel>() {                        @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<SocketChannel>() {                        @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中的应用。


原创粉丝点击