文章目录

  • 一、什么是粘包和拆包
  • 二、粘包和拆包示例代码
    • 1、TimeServerHandler
    • 2、TimeClientHandler
  • 三、使用Netty解决粘包和拆包
    • 1、TimeServerHandler中的ChildChannelHandler
    • 2、TimeClientHandler
  • 四、LineBasedFrameDecoder和StringEncoder

一、什么是粘包和拆包

先从数据的发送和接收讲起, Netty 发送和读取数据的单位,使用 ByteBuf 来充当。每一次发送,就是向Channel 写入一个 ByteBuf ;每一次读取,就是从 Channel 读到一个 ByteBuf 。

发送一次数据调用如下方法:

channel.writeAndFlush(buffer);

读取一次数据调用如下方法:

public void channelRead(ChannelHandlerContext ctx, Object msg)
{ByteBuf byteBuf = (ByteBuf) msg;//....
}

我们理想是:发送端每发送一个buffer,接收端就能接收到一个一模一样的buffer。

但是实际通讯情况可能不能很好的满足,所以就会发生粘包和拆包。

  1. 粘包指接收端读取的时候,多个发送过来的 ByteBuf “粘”在了一起。换句话说,接收端读取一次的 ByteBuf ,读到了多个发送端的 ByteBuf ,是为粘包。
  2. 就是接收端将一个发送端的ByteBuf “拆”开了,形成一个破碎的包,我们定义这种 ByteBuf 为半包。
    换句话说,接收端读取一次的 ByteBuf ,读到了发送端的一个 ByteBuf的一部分,是为半包。

更加深理解这两个词,我们把Netty权威指南图拿过来看下:

  • 服务端分两次读到了两个独立的数据包,分别是D1和D2,没有粘包和拆包
  • 服务端一次接收到了两个数据包,D1和D2粘在一起,发生了粘包
  • 服务端分两次读到了两个数据包,一次读到了完整的D1包和D2的部分包D2_1,第二次读到了D2剩下的包D2_2
  • 服务端分两次读到了两个数据包,一次读到了D1的部分分D1_1,第二次读到了D1的剩下的包D1_2和完整的D2包

二、粘包和拆包示例代码

把上一篇文章的服务端和客户端修改下

1、TimeServerHandler

public class TimeServerHandler extends ChannelInboundHandlerAdapter {private int counter;/*** 收到客户端消息,自动触发* @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {//将 msg 转为 Netty 的 ByteBuf 对象,类似 JDK 中的 java.nio.ByteBuffer,不过 ButeBuf 功能更强,更灵活ByteBuf buf = (ByteBuf) msg;//readableBytes:获取缓冲区可读字节数,然后创建字节数组//从而避免了像 java.nio.ByteBuffer 时,只能盲目的创建特定大小的字节数组,比如 1024byte[] req = new byte[buf.readableBytes()];//readBytes:将缓冲区字节数组复制到新建的 byte 数组中//然后将字节数组转为字符串buf.readBytes(req);String body = new String(req, "UTF-8").substring(0,req.length-System.getProperty("line.separator").length());System.out.println("The time server receive order : " + body + "; the counter is :" + (++counter));//回复消息//copiedBuffer:创建一个新的缓冲区,内容为里面的参数//通过 ChannelHandlerContext 的 write 方法将消息异步发送给客户端*/String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());ctx.write(resp);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {//flush:将消息发送队列中的消息写入到 SocketChannel 中发送给对方,为了频繁的唤醒 Selector 进行消息发送//Netty 的 write 方法并不直接将消息写如 SocketChannel 中,调用 write 只是把待发送的消息放到发送缓存数组中,再通过调用 flush//方法,将发送缓冲区的消息全部写入到 SocketChannel 中ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {/**当发生异常时,关闭 ChannelHandlerContext,释放和它相关联的句柄等资源 */ctx.close();}
}

2、TimeClientHandler

public class TimeClientHandler extends ChannelInboundHandlerAdapter {private int counter;private byte[] req;/*** Creates a client-side handler.*/public TimeClientHandler() {req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();}/*** 当客户端和服务端 TCP 链路建立成功之后,Netty 的 NIO 线程会调用 channelActive 方法* @param ctx*/@Overridepublic void channelActive(ChannelHandlerContext ctx) {ByteBuf message = null;for (int i = 0;i<100;i++) {message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}}/*** 当服务端返回应答消息时,channelRead 方法被调用,从 Netty 的 ByteBuf 中读取并打印应答消息* @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, "UTF-8");System.out.println("Now is : " + body + "this counter is:" + (++counter));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// 释放资源System.out.println("Unexpected exception from downstream : "+ cause.getMessage());ctx.close();}
}

运行程序之后服务端和客户端打印结果为:


说明以上程序发生了粘包。

三、使用Netty解决粘包和拆包

还使用上一节示例代码

1、TimeServerHandler中的ChildChannelHandler

public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {protected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));socketChannel.pipeline().addLast(new StringEncoder());socketChannel.pipeline().addLast(new TimeServerHandler());}
}

2、TimeClientHandler

public class TimeClientHandler extends ChannelInboundHandlerAdapter {private int counter;private byte[] req;/*** Creates a client-side handler.*/public TimeClientHandler() {req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();}/*** 当客户端和服务端 TCP 链路建立成功之后,Netty 的 NIO 线程会调用 channelActive 方法* @param ctx*/@Overridepublic void channelActive(ChannelHandlerContext ctx) {ByteBuf message = null;for (int i = 0;i<100;i++) {message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}}/*** 当服务端返回应答消息时,channelRead 方法被调用,从 Netty 的 ByteBuf 中读取并打印应答消息* @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {ByteBuf body = (ByteBuf) msg;System.out.println("Now is : " + body + "this counter is:" + (++counter));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// 释放资源System.out.println("Unexpected exception from downstream : "+ cause.getMessage());ctx.close();}
}

四、LineBasedFrameDecoder和StringEncoder

LineBasedFrameDecoder工作原理是依次遍历ByteBuf中可读字节,判断是否有"\n","\r\n"。如果有,就以此位置为结束位置。它是以换行符为结束的解码器。支持携带结束符或者不带结束符两种方式。同时支持配置行数最大长度。如果读取到最大行数还没有发现换行符,就会抛出异常,同时忽略掉以前的异常码流。

Netty 框架学习(二):Netty粘包和拆包相关推荐

  1. java nio 客户端_Java网络编程:Netty框架学习(二)---Java NIO,实现简单的服务端客户端消息传输...

    概述 上篇中已经讲到Java中的NIO类库,Java中也称New IO,类库的目标就是要让Java支持非阻塞IO,基于这个原因,更多的人喜欢称Java NIO为非阻塞IO(Non-Block IO), ...

  2. netty编解码器注意事项及粘包和拆包解决方案

    netty编解码器 当 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换.入站消息会被解码:从字节转换为另一种格式(比如 java 对象):如果是出站消息,它会被编码成字节. Netty ...

  3. Netty4 学习笔记之三-粘包和拆包

    名词解释 粘包: 会将消息粘粘起来发送.类似吃米饭,一口吃多个饭粒,而不是一粒一粒的吃. 拆包: 会将消息拆开,分为多次接受.类似喝饮料,一口一口的喝,而不是一口气喝完. 简单的来说: 多次发送较少内 ...

  4. Netty 框架学习(二):DelimiterBasedFrameDecoder和FixedLengthFrameDecoder

    文章目录 一.DelimiterBasedFrameDecoder 服务端 1.EchoServer 2.EchoServerChannelHandler 3.EchoServerHandler 客户 ...

  5. 面试官问:你来讲下Netty通信中的粘包、拆包?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Java技术剑 来源:https://urlify.cn/I ...

  6. 【Netty】TCP粘包和拆包

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

  7. Netty 解决粘包和拆包问题的四种方案

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://my.oschina.net/ ...

  8. 【博学谷学习记录】超强总结,用心分享 | 架构师 Netty框架学习总结

    文章目录 前言 一.Netty · Netty简介 · Netty核心架构 · Netty中Reactor实现 1.工作流程 2.ChannelPipeline 和 ChannelHandler(开发 ...

  9. netty框架学习及springboot整合集成

    netty框架学习及springboot整合集成 1. Netty基本概念 2. Netty框架 2.1 Netty框架结构 2.1 Netty NIO 2.2 Reactor线程模型 3. Spri ...

最新文章

  1. linux 故障注入_阿里巴巴开源故障注入工具_chaosblade
  2. 实现软件自动启动代码
  3. 浅谈redis数据库的键值设计
  4. VS2008下编的程序生成的EXE 在没有安装VS2008的计算机上能运行
  5. qaxwidget传递参数到html,记一次QT使用QAxWidget打开.html文件调用显示离线百度地图不能缩放,自定义图片不能显示解决方法...
  6. MIPI CSI-2规范一——概述及层级
  7. python预测数据怎么写_Python代写时间序列选择波动率预测指数收益算法分析案例...
  8. 面试的那些事(收藏类)
  9. 中国兽医显微镜市场趋势报告、技术动态创新及市场预测
  10. Hello World, Hello Me | 图灵人工智能书单
  11. 微信小助手WeChatExtension中文版安装教程
  12. ForkJoinPool的使用及基本原理
  13. [TI TDA4 J721E] TDA4平台 相关技术文章 汇总
  14. 计算机网络课设路由器,计算机网络课程设计---小型校园网络设计.doc
  15. .php文件是病毒吗,php病毒
  16. 软件实习项目2——贪吃喵(猫吃鱼版贪吃蛇)(代码实现)
  17. android 4.2.2进度条,进度条(ProgressBar)——Mars Android开发视频之第一季第十七集(重)...
  18. ElasticSerach安装IK中文分词器,并在Java中使用
  19. javafx 教程_JavaFX技巧32:需要图标吗? 使用Ikonli!
  20. 网易笔试题2:会话列表

热门文章

  1. php 分布式环境 启动,极简.高性能.分布式框架,可运行于多种环境(apache/php-fpm,swoole)...
  2. 计算机控制读书报告心得,计算机控制系统读书报告
  3. 前端ui 后台管理系统 简洁_Github上前端不可不知的可视化后台管理系统(1)
  4. rust(14)-if let,while let
  5. 【学术相关】TopPaper:AI 初学者经典论文列表
  6. 指针都没搞懂,还能算得上 C++ 老司机?
  7. 【机器学习基础】数学推导+纯Python实现机器学习算法25:CatBoost
  8. 成年人的低头,从拼多多开始
  9. KDD Cup 2021:时间序列异常检测问题开源方案
  10. 三年深入探索,网易云信让在线医疗做到技术“在线”