前言

上一篇Netty 心跳 demo 中,了解了Netty中的客户端和服务端之间的心跳。这篇就来讲讲Netty中的粘包和拆包以及相应的处理。

名词解释

粘包: 会将消息粘粘起来发送。类似吃米饭,一口吃多个饭粒,而不是一粒一粒的吃。
拆包: 会将消息拆开,分为多次接受。类似喝饮料,一口一口的喝,而不是一口气喝完。

简单的来说:
多次发送较少内容,会发生粘包现象。
单次发送内容过多,会发生拆包现象。

我们使用简单的Netty的服务端和客户端demo用来测试粘包和拆包。
Hello Netty 发送一百次,就会发送粘包现象;
将《春江花月夜》和《行路难》发送一次就会发送拆包现象;

示例图:

粘包:

拆包:

解决粘包、拆包

因为Netty已经提供了几个常用的解码器,帮助我们解决这些问题,所以我们不必再去造轮子了,直接拿来用就好了。

解决粘包

在Server服务端使用定长数据帧的解码器 FixedLengthFrameDecoder之后。
可以明显看到数据已经按照我们所设定的大小分割了。

解决拆包

在Server服务端使用字节解码器 LineBasedFrameDecoder 之后。
由于字节已经超过我们设置的最大的字节数,所以报错了。

所以,我们发送的字节在设置的范围内的话,就可以看到拆包现象已经解决。

Netty还提供了一个HttpObjectAggregator类用于解决粘包、拆包现象。
以下摘自Netty官方文档

  1. 如果对于单条HTTP消息你不想处理多个消息对象,你可以传入 HttpObjectAggregator pipline中。HttpObjectAggregator 会将多个消息对象转变为单个 FullHttpRequest 或者 FullHttpResponse

使用HttpObjectAggregator 之后

可以看到,粘包和拆包现象得到了改善。

那么开始贴代码,几乎和之前的demo一样。

服务端:

  1. import io.netty.bootstrap.ServerBootstrap;
  2. import io.netty.channel.ChannelFuture;
  3. import io.netty.channel.EventLoopGroup;
  4. import io.netty.channel.nio.NioEventLoopGroup;
  5. import io.netty.channel.socket.nio.NioServerSocketChannel;
  6. /**
  7. *
  8. * Title: NettyServer
  9. * Description: Netty服务端
  10. * Version:1.0.0
  11. * @author pancm
  12. * @date 2017年10月8日
  13. */
  14. public class NettyServer {
  15. private static final int port = 6789; //设置服务端端口
  16. private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接
  17. private static ServerBootstrap b = new ServerBootstrap();
  18. /**
  19. * Netty创建全部都是实现自AbstractBootstrap。
  20. * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。
  21. **/
  22. public static void main(String[] args) throws InterruptedException {
  23. try {
  24. b.group(group);
  25. b.channel(NioServerSocketChannel.class);
  26. b.childHandler(new NettyServerFilter()); //设置过滤器
  27. // 服务器绑定端口监听
  28. ChannelFuture f = b.bind(port).sync();
  29. System.out.println("服务端启动成功,端口是:"+port);
  30. // 监听服务器关闭监听
  31. f.channel().closeFuture().sync();
  32. }catch(Exception e){
  33. e.printStackTrace();
  34. }
  35. finally {
  36. group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程
  37. }
  38. }
  39. }
  1. mport io.netty.channel.ChannelInitializer;
  2. import io.netty.channel.ChannelPipeline;
  3. import io.netty.channel.socket.SocketChannel;
  4. import io.netty.handler.codec.FixedLengthFrameDecoder;
  5. import io.netty.handler.codec.LineBasedFrameDecoder;
  6. import io.netty.handler.codec.http.HttpObjectAggregator;
  7. import io.netty.handler.codec.string.StringDecoder;
  8. import io.netty.handler.codec.string.StringEncoder;
  9. /**
  10. *
  11. * Title: HelloServerInitializer
  12. * Description: Netty 服务端过滤器
  13. * Version:1.0.0
  14. * @author pancm
  15. * @date 2017年10月8日
  16. */
  17. public class NettyServerFilter extends ChannelInitializer<SocketChannel> {
  18. @Override
  19. protected void initChannel(SocketChannel ch) throws Exception {
  20. ChannelPipeline ph = ch.pipeline();
  21. // 解码和编码,应和客户端一致
  22. // ph.addLast(new FixedLengthFrameDecoder(100)); //定长数据帧的解码器 ,每帧数据100个字节就切分一次。 用于解决粘包问题
  23. // ph.addLast(new LineBasedFrameDecoder(2048)); //字节解码器 ,其中2048是规定一行数据最大的字节数。 用于解决拆包问题
  24. ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));
  25. ph.addLast("decoder", new StringDecoder());
  26. ph.addLast("encoder", new StringEncoder());
  27. ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑
  28. }
  29. }
  1. import io.netty.channel.ChannelHandlerContext;
  2. import io.netty.channel.ChannelInboundHandlerAdapter;
  3. import java.net.InetAddress;
  4. /**
  5. *
  6. * Title: HelloServerHandler
  7. * Description: 服务端业务逻辑 粘包、拆包测试
  8. * Version:1.0.0
  9. * @author pancm
  10. * @date 2017年10月8日
  11. */
  12. public class NettyServerHandler extends ChannelInboundHandlerAdapter {
  13. /** 条数 */
  14. private int count=0;
  15. /**
  16. * 业务逻辑处理
  17. */
  18. @Override
  19. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  20. String body = (String)msg;
  21. System.out.println("接受的数据是: " + body + ";条数是: " + ++count);
  22. }
  23. /**
  24. * 建立连接时,返回消息
  25. */
  26. @Override
  27. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  28. System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
  29. ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! ");
  30. super.channelActive(ctx);
  31. }
  32. }

客户端

  1. import io.netty.bootstrap.Bootstrap;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.Channel;
  5. import io.netty.channel.EventLoopGroup;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.nio.NioSocketChannel;
  8. import java.io.IOException;
  9. /**
  10. *
  11. * Title: NettyClient
  12. * Description: Netty客户端 粘包、拆包测试
  13. * Version:1.0.0
  14. * @author pancm
  15. * @date 2017年10月16日
  16. */
  17. public class NettyClient {
  18. public static String host = "127.0.0.1"; //ip地址
  19. public static int port = 6789; //端口
  20. /// 通过nio方式来接收连接和处理连接
  21. private static EventLoopGroup group = new NioEventLoopGroup();
  22. private static Bootstrap b = new Bootstrap();
  23. private static Channel ch;
  24. /**
  25. * Netty创建全部都是实现自AbstractBootstrap。
  26. * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。
  27. **/
  28. public static void main(String[] args) throws InterruptedException, IOException {
  29. System.out.println("客户端成功启动...");
  30. b.group(group);
  31. b.channel(NioSocketChannel.class);
  32. b.handler(new NettyClientFilter());
  33. // 连接服务端
  34. ch = b.connect(host, port).sync().channel();
  35. star(3);
  36. }
  37. public static void star(int i) throws IOException{
  38. String str="春江潮水连海平,海上明月共潮生。"
  39. +" 滟滟随波千万里,何处春江无月明! "
  40. +" 江流宛转绕芳甸,月照花林皆似霰;"
  41. +" 空里流霜不觉飞,汀上白沙看不见。"
  42. +" 江天一色无纤尘,皎皎空中孤月轮。"
  43. +" 江畔何人初见月?江月何年初照人?"
  44. +" 人生代代无穷已,江月年年望相似。"
  45. +" 不知江月待何人,但见长江送流水。"
  46. +" 白云一片去悠悠,青枫浦上不胜愁。"
  47. +" 谁家今夜扁舟子?何处相思明月楼?"
  48. +" 可怜楼上月徘徊,应照离人妆镜台。"
  49. +" 玉户帘中卷不去,捣衣砧上拂还来。"
  50. +" 此时相望不相闻,愿逐月华流照君。"
  51. +" 鸿雁长飞光不度,鱼龙潜跃水成文。"
  52. +" 昨夜闲潭梦落花,可怜春半不还家。"
  53. +" 江水流春去欲尽,江潭落月复西斜。"
  54. +" 斜月沉沉藏海雾,碣石潇湘无限路。"
  55. +" 不知乘月几人归,落月摇情满江树。"
  56. +" 噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。"
  57. +" 西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。"
  58. +" 黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。"
  59. +" 问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。"
  60. +" 蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒挂倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。"
  61. +" 其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。"
  62. +" 所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。"
  63. +" 蜀道之难,难于上青天,侧身西望长咨嗟!";
  64. if(i==1){
  65. for(int j=0;j<100;j++){
  66. str="Hello Netty";
  67. ch.writeAndFlush(str);
  68. }
  69. }else if(i==2){
  70. str+=str;
  71. ch.writeAndFlush(str);
  72. }else if(i==3){
  73. //System.getProperty("line.separator") 结束标记
  74. byte [] bt=(str+System.getProperty("line.separator")).getBytes();
  75. ByteBuf message = Unpooled.buffer(bt.length);
  76. message.writeBytes(bt);
  77. ch.writeAndFlush(message);
  78. }
  79. System.out.println("客户端发送数据:"+str+",发送数据的长度:"+str.length());
  80. }
  81. }
  1. import io.netty.channel.ChannelInitializer;
  2. import io.netty.channel.ChannelPipeline;
  3. import io.netty.channel.socket.SocketChannel;
  4. import io.netty.handler.codec.string.StringDecoder;
  5. import io.netty.handler.codec.string.StringEncoder;
  6. /**
  7. *
  8. * Title: NettyClientFilter
  9. * Description: Netty客户端 过滤器
  10. * Version:1.0.0
  11. * @author pancm
  12. * @date 2017年10月8日
  13. */
  14. public class NettyClientFilter extends ChannelInitializer<SocketChannel> {
  15. @Override
  16. protected void initChannel(SocketChannel ch) throws Exception {
  17. ChannelPipeline ph = ch.pipeline();
  18. /*
  19. * 解码和编码,应和服务端一致
  20. * */
  21. ph.addLast("decoder", new StringDecoder());
  22. ph.addLast("encoder", new StringEncoder());
  23. ph.addLast("handler", new NettyClientHandler()); //客户端的逻辑
  24. }
  25. }
  1. import java.util.Date;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. /**
  5. *
  6. * Title: NettyClientHandler
  7. * Description: 客户端业务逻辑实现
  8. * Version:1.0.0
  9. * @author pancm
  10. * @date 2017年10月8日
  11. */
  12. public class NettyClientHandler extends ChannelInboundHandlerAdapter {
  13. /**
  14. * 业务逻辑处理
  15. */
  16. @Override
  17. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  18. System.out.println("客户端接受的消息:"+msg);
  19. }
  20. /**
  21. * 建立连接时
  22. */
  23. @Override
  24. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  25. System.out.println("建立连接时:"+new Date());
  26. ctx.fireChannelActive();
  27. }
  28. /**
  29. *
  30. * 关闭连接时
  31. */
  32. @Override
  33. public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  34. System.out.println("关闭连接时:"+new Date());
  35. }
  36. }
  37. 该项目我放在github上了,有兴趣的可以看看!https://github.com/xuwujing/Netty

Netty4 学习笔记之三:粘包和拆包的更多相关文章

  1. Netty学习(四)-TCP粘包和拆包

    我们都知道TCP是基于字节流的传输协议.那么数据在通信层传播其实就像河水一样并没有明显的分界线,而数据具体表示什么意思什么地方有句号什么地方有分号这个对于TCP底层来说并不清楚.应用层向TCP层发送用 ...

  2. C#网络编程学习(5)---Tcp连接中出现的粘包、拆包问题

    本文参考于CSDN博客wxy941011 1.疑问 我们使用第四个博客中的项目. 修改客户端为:连接成功后循环向服务器发送从1-100的数字.看看服务器会不会正常的接收100次数据. 可是我们发现服务 ...

  3. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

  4. TCP粘包,拆包及解决方法

    在进行Java NIO学习时,发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题.我们都知道TCP属于传输 ...

  5. [转帖]Linux学习笔记之rpm包管理功能全解

    Linux学习笔记之rpm包管理功能全解 https://www.cnblogs.com/JetpropelledSnake/p/11177277.html rpm 的管理命令 之前学习过 yum 的 ...

  6. netty 解决TCP粘包与拆包问题(二)

    TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识 ...

  7. netty 解决TCP粘包与拆包问题(一)

    1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...

  8. 关于TCP的粘包和拆包

    问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...

  9. TCP粘包的拆包处理

    因为TCP是流式处理的,所以包没有边界,必须设计一个包头,里面表示包的长度(一般用字节表示),根据这个来逐个拆包.如果对于发送/接收频率不高的话,一般也就不做拆包处理了,因为不大可能有粘包现象. 以下 ...

随机推荐

  1. 采访 Lua 发明人的一篇文章

    采访 Lua 发明人的一篇文章 来源 https://blog.codingnow.com/2010/06/masterminds_of_programming_7_lua.html <Mast ...

  2. Bridging signals(NlogN最长上升子序列)

    Bridging signals Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  3. MQTT——订阅报文

    我们已经把相关的连接报文搞定了.笔者想来想去还是决定先讲解一下订阅报文(SUBSCRIBE ).如果传统的通信方式是客户端和服务端之间一般就直接传输信息.但是MQTT的通信方式是通过发布/订阅的方式进 ...

  4. ⑧bootstrap组件 文字图片 下拉菜单 按钮组 使用基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Linux上MySQL的安装

    a.检测是否安装了mysql rpm  -qa | grep mysql b.卸载系统自带的mysql rpm -e --nodeps 卸载的包 rpm -e --nodeps mysql-libs- ...

  6. python20171113笔记

    问题一: python {File "<stdin>", line 1} error 解决方法:就是不用先输入 python进入python解释器,而直接输入pytho ...

  7. win10出现"本地计算机上的MySQL57服务启动后停止"

    在window10下mysql57出现"本地计算机上的MySQL57服务启动后停止.某些服务在未由其他服务或程序使用时将自动停止"错误 环境:win10.MySQL Communi ...

  8. docker下编译mangoszero WOW60级服务端(一)

    这几天看到暴雪准备开放怀旧服的新闻,突然想到几年前用大芒果window一键服务端自己搭建过服务,就想着在Linux环境下重新编译一套,毕竟Linux作为服务端,性能和稳定性都会高一些,于是在mac虚拟 ...

  9. C#对注册表的操作

    C#中提供的与注册表相关的最主要的是两个类: Registry 和 RegistryKey,这两个类属于Microsoft.Win32命名空间 Registry类包含5个公共的静态域,分别代表5个基本 ...

  10. SpringMVC注解HelloWorld

    今天整理一下SpringMVC注解 欢迎拍砖 @RequestMapping RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是 ...