当前位置: 首页 > news >正文

粘包、半包和Netty中的自定义帧解码器间的关系

        之前学习Netty的时候学到自定义编解码器这一部分后就没再继续学习,同时对于这部分知识学习不深入。一直有个误区:自定义编码以及解码服务器就能够解决TCP作为流式协议传输(无消息边界)导致的粘包、半包问题。实则上面这句话有非常大的问题,今日再学习了粘包、半包以及自定义编解码器的关系,纠正已有部分错误认识。

        之前写过一篇Netty中的粘包、半包以及另一篇自定义编解码器的文章:

Netty中半包粘包的产生与处理:短连接、固定长度、固定分隔符、预设长度;redis、http协议举例;网络数据的发送和接收过程_discarded inbound message pooledslicedbytebuf-CSDN博客

自定义协议编解码:Codec(encode\decode)及Sharble注解详解_自定义解码-CSDN博客 

        借此机会对上面内容进行补充以及纠正。

粘包现象以及产生原因

        现象:发送 abc def,接收 abcdef。

        原因:应用层、滑动窗口、Nagle算法。

  • 应用层:接收方 ByteBuf 设置太大(Netty 默认 1024)

  • 滑动窗口:假设发送方 256 bytes 表示一个完整报文,但由于接收方处理不及时且窗口大小足够大,这 256 bytes 字节就会缓冲在接收方的滑动窗口中,当滑动窗口中缓冲了多个报文就会粘包

  • Nagle 算法:会造成粘包

        Nagle算法:也称为延迟确认算法(Delayed Acknowledgment Algorithm)。

        主要目的:减少因频繁发送小数据包而造成的网络拥塞。在TCP连接中,如果应用程序发送了一系列小的数据包,这些数据包可能会在网络中引起额外的开销。Nagle算法通过以下机制来优化数据传输:

        实现方式:

  1. 数据合并:当应用程序连续发送小的数据包时,Nagle算法会将这些数据包合并为一个较大的数据包,然后一起发送。这样可以减少发送的数据包数量,从而减少网络拥塞。

  2. 拥塞窗口:TCP使用拥塞窗口(Congestion Window)来控制发送数据的速率。Nagle算法会根据拥塞窗口的大小来决定是否延迟发送数据包。

  3. 确认等待:当TCP接收到数据包时,它会发送一个确认(ACK)给发送方。Nagle算法会延迟发送这个确认,直到有足够的数据来填充一个最大大小的TCP段,或者直到一个特定的时间间隔过去。

  • 即使发送一个字节,也需要加入 tcp 头和 ip 头,也就是总字节数会使用 41 bytes,非常不经济。因此为了提高网络利用率,tcp 希望尽可能发送足够大的数据,这就是 Nagle 算法产生的缘由

  • 该算法是指发送端即使还有应该发送的数据,但如果这部分数据很少的话,则进行延迟发送

    • 如果 SO_SNDBUF 的数据达到 MSS,则需要发送

    • 如果 SO_SNDBUF 中含有 FIN(表示需要连接关闭)这时将剩余数据发送,再关闭

    • 如果 TCP_NODELAY = true,则需要发送

    • 已发送的数据都收到 ack 时,则需要发送

    • 上述条件不满足,但发生超时(一般为 200ms)则需要发送

    • 除上述情况,延迟发送

半包现象以及产生原因 

        现象:发送 abcdef,接收 abc def

        原因:应用层、滑动窗口、MSS限制。

  • 应用层:接收方 ByteBuf 小于实际发送数据量

  • 滑动窗口:假设接收方的窗口只剩了 128 bytes,发送方的报文大小是 256 bytes,这时放不下了,只能先发送前 128 bytes,等待 ack 后才能发送剩余部分,这就造成了半包

  • MSS 限制:当发送的数据超过 MSS 限制后,会将数据切分发送,就会造成半包

        MSS和MTU的关系:

         MTU(maximum transmission unit):链路层对一次能够发送的最大数据有限制,这个限制称之为MTU,不同的链路设备的MTU值也不相同。

        MSS(maximum segment size)最大段长度:它是MTU刨去tcp头和ip头后剩余能够作为数据传输的字节数。

  • ipv4 tcp 头占用 20 bytes,ip 头占用 20 bytes,因此以太网 MSS 的值为 1500 - 40 = 1460

  • TCP 在传递大量数据时,会按照 MSS 大小将数据进行分割发送

  • MSS 的值在三次握手时通知对方自己 MSS 的值,然后在两者之间选择一个小值作为 MSS

 

粘包、半包解决方案

  • 短连接:能够很好解决粘包,对于半包解决不是很友好(当需要发送字节数超过能够发送最大字节数时就会产生半包)。
  • 固定长度:如果发送数据达不到预设长度,需要填充特定字符,浪费带宽。
  • 固定分隔符:对于消息中含有固定分隔符的消息不友好,无法识别这个分隔符是消息体还是消息边界。
  • 预设长度:每一条消息包含head以及body,head中包含body的长度。

        具体实现方案参考上面博客链接。

自定义编解码器和粘包、半包的关系

        自定义编解码器就是上面提到的第四种实现方案(head[length] body),貌似感觉非常正确不存在问题。

        对于粘包,自定义解码器能够很好处理,此时接收到的消息总量肯定大于需要解析的消息长度,此时不会产生问题。

        对于半包,如果此时发送方一条消息由于某种原因划分为两次发送,此时第一次发送后接收到接收到消息后,首先解析出消息头中的长度,直接使用解析到的长度提取对应的字节数,然而此时并不能提取到对应长度的字节数,此时就会反序列化失败。并不能处理此种情况的半包。

        测试代码:

        下面红色方框中框住的内容即为通过Netty的slice将一条encode后的消息(Message->ByteBuf)划分为两个ByteBuf。如果此时没有上面方框框住的帧解码器,直接调用一次slice后获取到s1,就对此ByteBuf执行channel.writeInbound(s1),由于消息体中没有长度的字节,此时解码失败。

        如果添加上第一个红色方框选中的 LengthFieldBasedFrameDecoder,按照上述方式执行两次slice后依次对于两个ByteBuf执行channel.writeInbound(),此时第一个执行成功后并不会立即解码,而是在第二个channel.writeInbound()执行成功后再解码。

        LengthFieldBasedFrameDecoder:是Netty网络编程的一部分,属于解码器(Decode)的一部分,主要作用将接收到的字节流划分为独立的帧(frames),对于处理基于长度的协议(例如某些二进制协议或自定义文本协议)非常有用。

        添加此组件后,当需要解码的消息长度没有达到指定长度时不会解码,会暂存在缓冲区中,继续等待消息的接收,当达到指定长度后才会将获取指定Length对应的字节数进行Decode。

LengthFieldBasedFrameDecoder详解

  • 基于长度字段的帧解码:这个解码器假设每个消息(或帧)的开始部分包含一个或多个字节,这些字节指示了随后消息体的长度。例如,一个消息可能以一个32位整数开始,表示后续数据的长度。

  • 自动帧拆分LengthFieldBasedFrameDecoder 会自动从接收到的字节流中拆分出完整的帧。它根据长度字段的值来确定帧的边界,并将完整的帧传递给下一个处理环节。

  • 处理粘包和半包问题:在网络编程中,经常会遇到粘包(多个帧粘连在一起)和半包(一个帧被分成多次接收)的问题。LengthFieldBasedFrameDecoder 通过长度字段来正确地拆分帧,从而解决了这些问题。、

  • 可配置的长度字段:开发者可以指定长度字段的偏移量、长度,以及是否包含长度字段本身的长度。这使得它能够适应不同的协议设计。

  • 灵活的解码策略:除了基本的长度字段解码,LengthFieldBasedFrameDecoder 还支持一些高级特性,如调整最大帧长度、处理过长的帧等。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Java设计模式】脏标记模式:通过变更跟踪优化性能
  • HTTP/3
  • 453.最小操作次数使数组元素相等
  • 产品概述Tektronix泰克TCP0030A电流探头TCP0030原装二手
  • 自然语言处理与深度学习的结合
  • pyinstaller 打包发布flask 应用
  • C++奇迹之旅:深度解析list的模拟实现
  • 【时时三省】(C语言基础)指针进阶6qsort函数的使用
  • BCC软译码和硬译码之间的性能差别
  • CAN协议通信 学习笔记
  • Linux启动流程和内核管理
  • 使用python导出Excel表格中的lua配置
  • 【网络安全 | 虚拟机】VMware Workstation Pro下载安装使用教程(免费版)
  • C语言深度复习【数组和指针】
  • 滚雪球学MyBatis-Plus(02):环境准备
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 【知识碎片】第三方登录弹窗效果
  • 4. 路由到控制器 - Laravel从零开始教程
  • angular组件开发
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Golang-长连接-状态推送
  • Java的Interrupt与线程中断
  • jquery cookie
  • maven工程打包jar以及java jar命令的classpath使用
  • Objective-C 中关联引用的概念
  • pdf文件如何在线转换为jpg图片
  • rabbitmq延迟消息示例
  • TypeScript迭代器
  • vue-cli在webpack的配置文件探究
  • webpack入门学习手记(二)
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 深度学习在携程攻略社区的应用
  • 使用agvtool更改app version/build
  • 微信开放平台全网发布【失败】的几点排查方法
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • # wps必须要登录激活才能使用吗?
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #Lua:Lua调用C++生成的DLL库
  • #NOIP 2014# day.2 T2 寻找道路
  • #数据结构 笔记三
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (06)Hive——正则表达式
  • (6) 深入探索Python-Pandas库的核心数据结构:DataFrame全面解析
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (转)jQuery 基础
  • (状压dp)uva 10817 Headmaster's Headache
  • *setTimeout实现text输入在用户停顿时才调用事件!*