七、分隔符和定长解码器的应用(1)

来源:互联网 发布:ubuntu命令行安装中文 编辑:程序博客网 时间:2024/05/16 14:44

TCP以流的方式进行数据传输,上层的应用协议为了对消息进行区分,往往采用如下四种方式:
(1)消息长度固定,累计读取到长度总和为定长LEN的报文后,就认为读取到了一个完整的信息;将计数器置位,重新开始读取下一个数据报;
(2)将回车换行符作为消息结束符,例如FTP协议,这种方式在文本协议中应用比较广泛;
(3)将特殊的分隔符作为消息的结束标志,回车换行符就是一种特殊的结束分隔符;
(4)通过对消息头中定义长度字段来标识消息的总长度。
Netty对上面四种情况,提供了4种对应的解码器,通过这些解码器,我们就可以不用对读取的报文人工解码,当然也不用考虑TCP的粘包好拆包问题。

前面使用的LineBasedFrameDecoder是根据换行符作为结束标志,下面我们提供了两种解码器:
DelimiterBasedFrameDecoder以分隔符做结束标志;
FixedLengthFrameDecoder自动完成对定长消息的解码;

一、DelimiterBasedFrameDecoder
EchoServer接收到EchoClient的请求消息后,将其打印出来,然后将原始消息返回给客户端,消息以“$_”作为分隔符。

工具:IntelliJ IDEA 2016.2.2(64)

netty版本:netty-all-5.0.0.Alpha1

服务端代码:

package NettyFrameDecoder;import io.netty.bootstrap.ServerBootstrap;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.DelimiterBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.logging.LoggingHandler;import sun.rmi.log.LogHandler;/** * Created by L_kanglin on 2017/6/12. * EchoServer接收到EchoClient的请求消息后,将其打印出来,然后将原始消息返回给 * 客户端,消息以“$_”作为分隔符 */public class EchoServer {    public void bind(int port) throws Exception {        //配置服务端的线程组        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(LogHandler.class))                .childHandler(new ChannelInitializer<SocketChannel>() {                    @Override                    protected void initChannel(SocketChannel ch) throws Exception {                        //创建分隔符缓冲对象,此处以“$_”分隔                        ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());                        //创建DelimiterBasedFrameDecoder对象,传入两个参数,1024表示单条消息的最大长度                        //当达到该长度仍没有分隔符则抛出toolong异常,第二个分隔符就是分隔符缓冲对象                        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));                        //将接收到的消息转化为字符串,将ByteBuf解码成字符串对象                        ch.pipeline().addLast((new StringDecoder()));                        //接收到的msg消息就是解码后的字符串对象                        ch.pipeline().addLast(new EchoServerHandler());                    }                });        //绑定端口,同步等待成功            ChannelFuture f =b.bind(port).sync();            //等待服务端监听端口关闭            f.channel().closeFuture().sync();        } finally{            bossGroup.shutdownGracefully();            workGroup.shutdownGracefully();        }    }    public static void main(String[] args) throws Exception {        int port=9030;        if(args !=null && args.length>0){            port=Integer.valueOf(args[0]);        }        new EchoServer().bind(port);    }}
package NettyFrameDecoder;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;/** * Created by L_kanglin on 2017/6/12. */public class EchoServerHandler extends ChannelHandlerAdapter {    int counter=0;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        //将接收到的消息打印出来,已经自动对消息进行了解码,所以msg是个完整的消息包        String body = (String) msg;        System.out.println("this is"+ ++counter+"times receive client:["+body+"]");        //由于设置过滤掉了分隔符,所以返回客户端的消息时,拼接分隔符"$_",最后创建ByteBuf,        //将原始消息重新返回给客户端        body+="$_";        ByteBuf echo= Unpooled.copiedBuffer(body.getBytes());        ctx.writeAndFlush(echo);    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close(); //发生异常,关闭链路    }}

客户端代码:

package NettyFrameDecoderClient;import io.netty.bootstrap.Bootstrap;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.DelimiterBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;/** * Created by L_kanglin on 2017/6/12. */public class EchoClient {    public void connnect(int port,String host) throws InterruptedException {        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 delimeter= Unpooled.copiedBuffer("$_".getBytes());                        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimeter));                        ch.pipeline().addLast(new StringDecoder());                        ch.pipeline().addLast(new EchoClientHandler());                    }                });        //发起异步连接操作            ChannelFuture f =b.connect(host,port).sync();        //等待客户端链路关闭            f.channel().closeFuture().sync();        } finally{            //优雅退出,释放NIO线程组            group.shutdownGracefully();        }    }    public static void main(String[] args) throws InterruptedException {        int port = 9030;        if(args !=null && args.length>0){            port=Integer.valueOf(args[0]);        }        new EchoClient().connnect(port,"127.0.0.1");    }}
package NettyFrameDecoderClient;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;/** * Created by L_kanglin on 2017/6/12. */public class EchoClientHandler extends ChannelHandlerAdapter {    private int counter;    static final String ECHO_REQ="Hi,Li Kanglin.Welcome to Netty.$_";    public EchoClientHandler() {    }   //在TCP链路建立成功之后循环发送请求消息给服务端    @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();    }}

服务端运行如下:

this is1times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is2times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is3times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is4times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is5times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is6times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is7times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is8times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is9times receive client:[Hi,Li Kanglin.Welcome to Netty.]this is10times receive client:[Hi,Li Kanglin.Welcome to Netty.]

客户端运行如下:

This is1time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is2time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is3time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is4time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is5time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is6time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is7time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is8time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is9time receive server:{ Hi,Li Kanglin.Welcome to Netty.}This is10time receive server:{ Hi,Li Kanglin.Welcome to Netty.}

如果没有解码器的处理,就会导致服务端一次读取了客户端发送的所有消息,也就是没有考虑到TCP粘包的问题。

原创粉丝点击