七、分隔符和定长解码器的应用(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粘包的问题。
阅读全文
0 0
- 七、分隔符和定长解码器的应用(1)
- Netty分隔符解码器和定长解码器的应用方案
- Netty的分隔符和定长解码器应用
- 八、分隔符和定长解码器的应用(2)
- Netty中分隔符和定长解码器的应用
- 分隔符和定长解码器在netty中的应用
- 《netty权威指南》5分隔符和定长解码器的应用
- Netty (四) 分隔符和定长解码器的使用
- [netty]--分隔符解码器DelimiterBasedFrameDecoder和定长解码器FixedLengthFrameDecoder
- Netty权威指南 第2版学习笔记5——分隔符和定长解码器的应用
- netty分割符和定长解码器的应用
- Netty权威指南之分隔符和定长解码器
- netty之分隔符和定长解码器解决之道
- Netty学习之旅(三)(分隔符和定长解码器)
- Netty的分隔符解码器的使用
- TStringList的分隔符应用
- 分割符和定长解码
- Delphi-IOCP研究笔记<七>===接收数据的解码器(Decoder)和数据处理
- Unity3D:延时方法Invoke和InvokeRepeating
- JVM GC解密
- 0018_4Sum
- Linux--信号屏蔽
- docker nginx实现一个主机部署多个站点
- 七、分隔符和定长解码器的应用(1)
- 几类自适应波束形成算法推导
- Windows平台装Mac OS(黑苹果)
- 程序调试优化二:使用Druid的数据库连接池和SQL监控
- 关于C++ const 的全面总结
- linux 命令和全称
- Android视频开发资料
- 超简单将Centos的yum源更换为国内的阿里云源
- Redis 常见 的几种使用场景