Netty4 学习笔记之三:粘包和拆包
来源:互联网 发布:linux书籍推荐 知乎 编辑:程序博客网 时间:2024/06/05 11:23
前言
在上一篇Netty 心跳 demo 中,了解了Netty中的客户端和服务端之间的心跳。这篇就来讲讲Netty中的粘包和拆包以及相应的处理。
名词解释
粘包: 会将消息粘粘起来发送。类似吃米饭,一口吃多个饭粒,而不是一粒一粒的吃。
拆包: 会将消息拆开,分为多次接受。类似喝饮料,一口一口的喝,而不是一口气喝完。
简单的来说:
多次发送较少内容,会发生粘包现象。
单次发送内容过多,会发生拆包现象。
我们使用简单的Netty的服务端和客户端demo用来测试粘包和拆包。
将 Hello Netty 发送一百次,就会发送粘包现象;
将《春江花月夜》和《行路难》发送一次就会发送拆包现象;
示例图:
粘包:
拆包:
解决粘包、拆包
因为Netty已经提供了几个常用的解码器,帮助我们解决这些问题,所以我们不必再去造轮子了,直接拿来用就好了。
解决粘包
在Server服务端使用定长数据帧的解码器 FixedLengthFrameDecoder之后。
可以明显看到数据已经按照我们所设定的大小分割了。
解决拆包
在Server服务端使用字节解码器 LineBasedFrameDecoder 之后。
由于字节已经超过我们设置的最大的字节数,所以报错了。
所以,我们发送的字节在设置的范围内的话,就可以看到拆包现象已经解决。
Netty还提供了一个HttpObjectAggregator类用于解决粘包、拆包现象。
以下摘自Netty官方文档
如果对于单条HTTP消息你不想处理多个消息对象,你可以传入 HttpObjectAggregator 到pipline中。HttpObjectAggregator 会将多个消息对象转变为单个 FullHttpRequest 或者 FullHttpResponse。
使用HttpObjectAggregator 之后
可以看到,粘包和拆包现象得到了改善。
那么开始贴代码,几乎和之前的demo一样。
服务端:
import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;/** * * Title: NettyServer* Description: Netty服务端* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyServer { private static final int port = 6789; //设置服务端端口 private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接 private static ServerBootstrap b = new ServerBootstrap(); /** * Netty创建全部都是实现自AbstractBootstrap。 * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 **/ public static void main(String[] args) throws InterruptedException { try { b.group(group); b.channel(NioServerSocketChannel.class); b.childHandler(new NettyServerFilter()); //设置过滤器 // 服务器绑定端口监听 ChannelFuture f = b.bind(port).sync(); System.out.println("服务端启动成功,端口是:"+port); // 监听服务器关闭监听 f.channel().closeFuture().sync(); }catch(Exception e){ e.printStackTrace(); } finally { group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 } }}
mport io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.FixedLengthFrameDecoder;import io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;/** * * Title: HelloServerInitializer * Description: Netty 服务端过滤器 * Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyServerFilter extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline ph = ch.pipeline(); // 解码和编码,应和客户端一致// ph.addLast(new FixedLengthFrameDecoder(100)); //定长数据帧的解码器 ,每帧数据100个字节就切分一次。 用于解决粘包问题 // ph.addLast(new LineBasedFrameDecoder(2048)); //字节解码器 ,其中2048是规定一行数据最大的字节数。 用于解决拆包问题 ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024)); ph.addLast("decoder", new StringDecoder()); ph.addLast("encoder", new StringEncoder()); ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑 } }
import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import java.net.InetAddress;/** * * Title: HelloServerHandler* Description: 服务端业务逻辑 粘包、拆包测试* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** 条数 */ private int count=0; /** * 业务逻辑处理 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String)msg; System.out.println("接受的数据是: " + body + ";条数是: " + ++count); } /** * 建立连接时,返回消息 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress()); ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! "); super.channelActive(ctx); }}
客户端
import io.netty.bootstrap.Bootstrap;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.Channel;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioSocketChannel;import java.io.IOException;/** * * Title: NettyClient* Description: Netty客户端 粘包、拆包测试* Version:1.0.0 * @author pancm* @date 2017年10月16日 */public class NettyClient { public static String host = "127.0.0.1"; //ip地址 public static int port = 6789; //端口 /// 通过nio方式来接收连接和处理连接 private static EventLoopGroup group = new NioEventLoopGroup(); private static Bootstrap b = new Bootstrap(); private static Channel ch; /** * Netty创建全部都是实现自AbstractBootstrap。 * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 **/ public static void main(String[] args) throws InterruptedException, IOException { System.out.println("客户端成功启动..."); b.group(group); b.channel(NioSocketChannel.class); b.handler(new NettyClientFilter()); // 连接服务端 ch = b.connect(host, port).sync().channel(); star(3); } public static void star(int i) throws IOException{ String str="春江潮水连海平,海上明月共潮生。" +" 滟滟随波千万里,何处春江无月明! " +" 江流宛转绕芳甸,月照花林皆似霰;" +" 空里流霜不觉飞,汀上白沙看不见。" +" 江天一色无纤尘,皎皎空中孤月轮。" +" 江畔何人初见月?江月何年初照人?" +" 人生代代无穷已,江月年年望相似。" +" 不知江月待何人,但见长江送流水。" +" 白云一片去悠悠,青枫浦上不胜愁。" +" 谁家今夜扁舟子?何处相思明月楼?" +" 可怜楼上月徘徊,应照离人妆镜台。" +" 玉户帘中卷不去,捣衣砧上拂还来。" +" 此时相望不相闻,愿逐月华流照君。" +" 鸿雁长飞光不度,鱼龙潜跃水成文。" +" 昨夜闲潭梦落花,可怜春半不还家。" +" 江水流春去欲尽,江潭落月复西斜。" +" 斜月沉沉藏海雾,碣石潇湘无限路。" +" 不知乘月几人归,落月摇情满江树。" +" 噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。" +" 西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。" +" 黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。" +" 问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。" +" 蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒挂倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。" +" 其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。" +" 所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。" +" 蜀道之难,难于上青天,侧身西望长咨嗟!"; if(i==1){ for(int j=0;j<100;j++){ str="Hello Netty"; ch.writeAndFlush(str); } }else if(i==2){ str+=str; ch.writeAndFlush(str); }else if(i==3){ //System.getProperty("line.separator") 结束标记 byte [] bt=(str+System.getProperty("line.separator")).getBytes(); ByteBuf message = Unpooled.buffer(bt.length); message.writeBytes(bt); ch.writeAndFlush(message); } System.out.println("客户端发送数据:"+str+",发送数据的长度:"+str.length()); }}
import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;/** * * Title: NettyClientFilter* Description: Netty客户端 过滤器* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyClientFilter extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline ph = ch.pipeline(); /* * 解码和编码,应和服务端一致 * */ ph.addLast("decoder", new StringDecoder()); ph.addLast("encoder", new StringEncoder()); ph.addLast("handler", new NettyClientHandler()); //客户端的逻辑 }}
import java.util.Date;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;/** * * Title: NettyClientHandler* Description: 客户端业务逻辑实现* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyClientHandler extends ChannelInboundHandlerAdapter { /** * 业务逻辑处理 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客户端接受的消息:"+msg); } /** * 建立连接时 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("建立连接时:"+new Date()); ctx.fireChannelActive(); } /** * * 关闭连接时 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("关闭连接时:"+new Date()); } }
阅读全文
1 0
- Netty4 学习笔记之三:粘包和拆包
- Netty4学习笔记(二) TCP黏包/拆包解决办法
- Netty学习之---TCP粘包和拆包
- Netty 权威指南笔记(三):TCP 粘包和拆包
- dbms_lob包学习笔记之三:instr和substr存储过程
- dbms_lob包学习笔记之三:instr和substr存储过程
- Swift2.2 学习笔记(三) ___函数和闭包
- 【Netty4.x】Netty TCP粘包/拆包问题的解决办法(二)
- Netty (三) TCP粘包和拆包解决方案
- Netty【三】 TCP 粘包和拆包
- Java学习笔记之包
- oracle学习笔记之包
- netty学习笔记(2)_tcp拆包/粘包问题
- jQuery学习笔记三:闭包
- python闭包学习笔记三
- gradle学习笔记(三) Groovy闭包
- Go学习笔记三(包)
- 黑马程序员-Java学习笔记之异常和包
- 优先队列
- 10月集训test8
- 一款开源Android在线音乐播放器
- [BZOJ]1799: [Ahoi2009]self 同类分布 数位DP
- 第三方高德开放平台高德地图定位
- Netty4 学习笔记之三:粘包和拆包
- 在MacOS Sierra上安装OpenCV
- OpenGL
- c++中四种强制类型转换(const_cast、static_cast应用最多)
- DNS配置和redhat的python2.6的升级
- 解读DES和SM4、RSA和SM2及SM3
- Spring项目简单配置(一)
- openmp常用指令(fortran版)(2)
- rosb笔记:bug时间:环境变量设置问题——“解决 roscd beginner_tutorials 没有此文件的问题”