Netty自定义应用层协议逃不开的粘包和拆包处理

这篇具有很好参考价值的文章主要介绍了Netty自定义应用层协议逃不开的粘包和拆包处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么场景会粘包和拆包

1.1 数据传输时粘包和拆包传输

导致一次发送的数据被分成多个数据包进行传输,或者多次发送的数据被粘成一个数据包进行传输

  1. 使用TCP进行数据传输时,TCP是一种有序的字节流,其中是一个一个的数据报文发送到系统的缓冲区中。因此在发送端和接收端之间无法保证数据的分割和边界。这就可能导致数据粘连在一起,或者被拆分成多个部分进行传输。所以基于TCP协议的应用层协议,要从TCP数据流中取得符合自己协议格式的Message 都需要面临粘包和拆包的问题。
  2. TCP是流协议,其中是一个个数据报文,所以他不会按照应用层的消息规范来对消息进行分隔。那么一个应用层消息,有可能一个应用层消息是占用了1.5个TCP报文,也有可能是2个应用层消息才占用1个TCP报文(TCP的Nagle算法就是这么工作的)。这个时候就需要针对这种拆包或者粘包情况来进行消息处理才能还原成应用层协议数据。

1.2 数据接收方读取数据拆包和粘包

接收方原因导致的粘包拆包是指在接收数据包时,由于接收端的应用程序读取速度过慢或Socket Buffer设置不当等原因,导致数据在Socket Buffer中积压造成一次读取多个数据包,或者一个数据包分成多次读取

  1. 应用程序读取速度过慢或缓冲区过大导致粘包:如果发送方发送3个每个长度为50的数据包,接收方每次从缓冲区读取100字节,第一次读取到50个字节后,过了很久再读取第二次。这时后两个数据包都到达了缓冲区被一次性读取了出去。这就发生了粘包!
  2. 应用程序读取过快或一次读取过小导致拆包:如果发送方发送3个长度100的数据包,接收方每次读取50个字节,那么数据包就不完整了。这就是发生了拆包!

1.3 理解总结

对于TCP传输过程中的粘包拆包:使用TCP来传输数据的话,TCP传输过程中发生粘包和拆包其实对我们应用层的协议关系不大。因为我们不直接接收和处理TCP报文,TCP报文由TCP协议自己处理。最后组装成发送时的字节流缓存到缓冲区。
对于读取数据发生的粘包拆包: 对我们有影响的粘包和拆包其实说的是我们应用层的协议的数据包 在读取中发生了粘包或者拆包,从而没法直接判断报文的边界,需要对这种拆包和粘包情况进行处理。来确定协议报文的边界来解析报文。



二、粘包拆包举例

情况1 : 应用层两个数据包,正好分隔成两个TCP数据包传输,并且分成了两个数据包读取。
此时不需要粘包或者拆包处理
image.png

情况2: 应用层两个数据包都很小,传输后被封装在了1个TCP数据包中,或者读取缓冲区时直接一次性都读取了。
这种情况,就出现了粘包 ,需要处理
image.png

情况3: 应用层传输两个数据包,其中一个较大被拆成了两个TCP数据包,读取时总共需要读取三个数据包。这种情况就是出现了拆包, 需要处理。
image.png


三、Netty拆包粘包现象案例

对粘包和拆包的理解
每次客户端向服务端发送数据,就相当于是通过TCP传输一段自定义格式的消息。那这个消息也可以看成我们自己的一个自定义应用层协议的一个数据报文。所以如果被服务端分多次解析或者多次发送被一次解析对我们应用层的协议来说就是发生了沾包和拆包了。

3.1 Netty 测试粘包现象

客户端代码:连接建立时发送10句打招呼的语句

/**
 * 通道激活时发送10句打招呼的内容
 * @param ctx ctx
 * @throws Exception 异常
 */
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    //连发10遍消息,判断服务端读取是否会粘包
    for (int i = 1; i <= 10; i++) {
        ChannelFuture channelFuture = ctx.writeAndFlush("Hello! 我是Netty客户端!");
    }

}

服务端代码:解析收到的消息内容,并且统计读取缓冲区的次数

int count=0;
/**
 * 通道读取事件
 * @param ctx
 * @param msg
 * @throws Exception
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("客户端发送过来的消息:" + msg);
    System.out.println("读取次数:"+(++count));
}

运行结果:十次发送的内容,发生了粘包被一次性读取出来了。

服务器启动成功。。。
解码器开始解码
客户端发送过来的消息:Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!Hello! 我是Netty客户端!
读取次数:1

3.2 Netty拆包测试

客户端代码:连接建立时发送足够大的数据,发送10次。如果服务端读取大于10次,说明每次发送的数据被拆包了。

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    //一次发送102400字节数据
    byte[] bytes = new byte[102400];
    Arrays.fill(bytes, (byte) 10);
    for (int i = 0; i < 10; i++) {
        ctx.writeAndFlush(Unpooled.copiedBuffer(bytes));
    }
}

服务端代码:读取完什么都不回应,看看读取这些数据每次读取的大小和需要读取的次数。

int count = 0;

/**
 * 通道读取事件
 * @param ctx
 * @param msg
 * @throws Exception
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ByteBuf byteBuf = (ByteBuf) msg;
    System.out.println("长度是:" + byteBuf.readableBytes());
    System.out.println("读取次数 = " + (++count));
}

运行结果:读取超过10次,说明发送的数据包被分成多次读取了 发生了拆包!

服务器启动成功。。。
长度是:2048
读取次数 = 1
长度是:32768
.......省略.......
读取次数 = 16
长度是:65536
读取次数 = 17
长度是:6144
读取次数 = 18



四、处理粘包和拆包

TCP协议是一个字节流协议,只负责将应用层协议发送的消息数据数据(例如Http请求报文)按照顺序传输并组装给接收端。在传输过程中,和收过程中都会发生粘包和拆包现象。所以对于上层应用层来说,不可能每次读取流就正好是一个应用层数据报文。所以就需要从一个数据流进行切割,切割出应用层报文的大小进行解析。

4.1 业内沾包和拆包问题的解决方案

  1. 固定长度消息体:累计读取固定长度数据就认为读取完成了一个数据报文。
  2. 换行符作为消息结束符:读取到换行符,就认为一个报文结束
  3. 使用特殊的符号作为结束符:方法2可以看成方法3的特例,使用这种方法需要保证消息体不包含这个特殊字符。
  4. 通过消息头中定义长度字段来标识消息的总长度。

4.2 Netty对解决方案的实现

Netty提供了四种解码器来解决粘包和拆包的问题,分别对应上面四种方案

  1. 固定长度的拆包器 FixedLengthFrameDecoder,每个应用层数据包的都拆分成都是固定长度的大小。解析时直接按照大小解析。
  2. 行拆包器 LineBasedFrameDecoder,每个应用层数据包,都以换行符作为分隔符,进行分割拆分。
  3. 分隔符拆包器 DelimiterBasedFrameDecoder,每个应用层数据包,都通过自定义的分隔符,进行分割拆分。
  4. 基于数据包长度的拆包器 LengthFieldBasedFrameDecoder,将应用层数据包的长度,作为接收端应用层数据包的拆分依据。按照应用层数据包的大小,拆包。这个拆包器,有一个要求,就是应用层协议中包含数据包的长度

4.3 对 3.1、3.2 中的案例拆包改造

使用行拆包器 LinebasedFrameDecoder 改造3.1

  1. pipeline中添加LinebasedFrameDecoder ch.pipeline().addLast(new LineBasedFrameDecoder(2048)); 这里2048是拆包器最大接受的数据大小,一次接收数据大于这个值超过会报错。
  2. 客户端发送消息增加换行 ctx.writeAndFlush(Unpooled.copiedBuffer("你好呀,我是Netty客户端"+i+"\n",CharsetUtil.UTF_8));

使用 DelimiterBasedFrameDecoder解码器 改造3.1
pipeline增加拆包解码器

//要用字节流中取得分隔符的字节段
ByteBuf byteBuf =Unpooled.copiedBuffer("$".getBytes(StandardCharsets.UTF_8));
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(2048, byteBuf));

客户端发送数据用特殊字符结尾文章来源地址https://www.toymoban.com/news/detail-421997.html

ctx.writeAndFlush(Unpooled.copiedBuffer("你好呀,我是Netty客户端"+i+"$",
CharsetUtil.UTF_8));

到了这里,关于Netty自定义应用层协议逃不开的粘包和拆包处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 应用层协议——https

    HTTP 协议内容都是按照⽂本的⽅式明⽂传输的,这就导致在传输过程中出现⼀些被篡改的情况。HTTPS 也是⼀个应⽤层协议,是在 HTTP 协议的基础上引⼊了⼀个加密层。HTTPS的端口号是443。 它是在应用层和传输层间加了一个软件层,当进行网络传输时,从上而下就是在加密,从

    2024年02月12日
    浏览(44)
  • 应用层协议 HTTP

    我们已经学过 TCP/IP , 已然知道数据能从客户端进程经过路径选择跨网络传送到服务器端进程。 我们还需要知道的是,我们把数据从 A 端传送到 B 端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用,所以我们还需要一层协议,不关心通信细节,关心应用

    2024年02月06日
    浏览(45)
  • 【网络】应用层——HTTPS协议

    🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言: 你只管努力,剩下的交给时间! 前面本喵讲解并演示了HTTP协议,在比较 POST 和 GET 方法的时候,本喵说这两个方法都不安全,虽然 POST 的提交的表单内容在请求正文中,无法在地址的 url 中看到,但是它仍然是不安全的。

    2024年02月14日
    浏览(44)
  • 【网络】应用层——HTTP协议

    🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言: 你只管努力,剩下的交给时间! 上篇文章中,本喵带着大家对HTTP有了一个初步的认识,今天就来详细讲解一下这个应用层协议。 如上图所示的 url (网址),里面包含有 / 以及 ? 等字符。 像这样的字符,已经被url当做 特殊

    2024年02月15日
    浏览(42)
  • 【网络原理】应用层协议 与 传输层协议

    ✨个人主页:bit me👇 ✨当前专栏:Java EE初阶👇 我们自己写的应用程序就是在应用层 虽然应用层里面有一些现成的协议,但是在实际工作中也会存在 自定义应用层协议 (发明协议? 协议就是约定,约定好客户端和服务器按照啥样的格式来传输数据 ) 那么应用层协议如何

    2023年04月20日
    浏览(49)
  • 【计算机网络】应用层协议 -- HTTP协议

    协议。网络协议的简称,网络协议是通信计算机双方必须共同遵守的一组约定,比如怎么建立连接,怎么互相识别等。 为了使数据在网络上能够从源头到达目的,网络通信的参与方必须遵守相同的规则,我们称这套相同的规则为协议(protocol),而协议最终都需要通过计算机

    2024年02月15日
    浏览(52)
  • JavaEE & HTTP应用层协议

    HTTP应用层协议 超文本传输协议(Hyper Text [Transfer Protocol](https://baike.baidu.com/item/Transfer Protocol/612755?fromModule=lemma_inlink),HTTP) 是一个简单的请求-响应协议 ,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以

    2024年02月06日
    浏览(83)
  • 【JavaEE】HTTP应用层协议

    HTTP应用层协议 超文本传输协议(Hyper Text [Transfer Protocol](https://baike.baidu.com/item/Transfer Protocol/612755?fromModule=lemma_inlink),HTTP) 是一个简单的请求-响应协议 ,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以

    2024年02月07日
    浏览(53)
  • 网络安全——应用层安全协议

    作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。   座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录  前言 一.应用层安全协议  1.应用层安全威胁 2.电子邮件安全协议 1.MIME协议 2.电子邮件安全威胁  3.S/MIME协议 4.PGP协议 本

    2024年02月06日
    浏览(57)
  • 网络协议(七)应用层-HTTP

    上篇文章介绍了传输层的TCP、UDP协议,在TCP/IP协议中,下三层(网络接口层,网络层,传输层)都是计算机系统联合其他硬件设备自己在干的事,身为程序员的我们平时对其感知不大。而应用层却是与程序开发息息相关的一层,如HTTP,HTTPS,DNS,FTP,SMTP等等,针对不同应用场

    2024年02月03日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包