Netty中解决粘包/半包

目录

什么是TCP粘包半包?

TCP 粘包/半包发生的原因

解决粘包半包

channelRead和channelReadComplete区别


什么是TCP粘包半包?

                

       假设客户端分别发送了两个数据包 D1 和 D2 给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下 4 种情况。

1. 服务端分两次读取到了两个独立的数据包,分别是 D1 和 D2,没有粘包和拆包。

2. 服务端一次接收到了两个数据包,D1 和 D2 粘合在一起,被称为 TCP 粘包。

3. 服务端分两次读取到了两个数据包,第一次读取到了完整的 D1 包和 D2 包的部分内容,第二次读取到了 D2 包的剩余内容,这被称为 TCP 拆包。

4. 服务端分两次读取到了两个数据包,第一次读取到了 D1 包的部分内容 D1_1,第二次读取到了 D1 包的剩余内容 D1_2 和 D2 包的整包。

       如果此时服务端 TCP 接收滑窗非常小,而数据包 D1 和 D2 比较大,很有可能会发生第五种可能,即服务端分多次才能将 D1 和 D2 包接收完全,期间发生多次拆包。


TCP 粘包/半包发生的原因

       由于 TCP 协议本身的机制(面向连接的可靠地协议-三次握手机制)客户端与服务器会维持一个连接(Channel),数据在连接不断开的情况下,可以持续不断地将多个数据包发往服务器,但是如果发送的网络数据包太小,那么他本身会启用 Nagle 算法(可配置是否启用)对较小的数据包进行合并(基于此,TCP 的网络延迟要 UDP 的高些)然后再发送(超时或者包大小足够)。那么这样的话,服务器在接收到消息(数据流)的时候就无法区分哪些数据包是客户端自己分开发送的,这样产生了粘包。服务器在接收到数据库后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现象。

       UDP:本身作为无连接的不可靠的传输协议(适合频繁发送较小的数据包),他不会对数据包进行合并发送(也就没有 Nagle 算法之说了),他直接是一端发送什么数据,直接就发出去了,既然他不会对数据合并,每一个数据包都是完整的(数据+UDP头+IP头等等发一次数据封装一次)也就没有粘包一说了。

分包产生的原因简单来说:

        应用程序写入数据的字节大小大于套接字发送缓冲区的大小,就是一个数据包被分成了多次接收。


解决粘包半包

解决方案:

1. 增加分割符。

客户端传送数据时带上分隔符,服务端接收数据时,通过分隔符判断数据是否发送完整。

核心代码如下:

 public static final String DELIMITER_SYMBOL = "@~";private static class ChannelInitializerImp extends ChannelInitializer<Channel> {@Overrideprotected void initChannel(Channel ch) throws Exception {ByteBuf delimiter = Unpooled.copiedBuffer(DELIMITER_SYMBOL.getBytes());ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));ch.pipeline().addLast(new DelimiterServerHandler());}}

2. 消息定长,例如每个报文的大小为固定长度 200 字节,如果不够,空位补空格。

   //客户端按照request的长度发送数据,服务端以request的长度接收数据public final static String REQUEST = "hello,netty!";private static class ChannelInitializerImp extends ChannelInitializer<Channel> {@Overrideprotected void initChannel(Channel ch) throws Exception {ch.pipeline().addLast(new FixedLengthFrameDecoder(FixedLengthEchoClient.REQUEST.length()));ch.pipeline().addLast(new FixedLengthServerHandler());}}//服务端按照response的长度发送数据,服务端以response的长度接收数据public static final String RESPONSE = "Welcome to Netty!";private static class ChannelInitializerImp extends ChannelInitializer<Channel> {@Overrideprotected void initChannel(Channel ch) throws Exception {ch.pipeline().addLast(new FixedLengthFrameDecoder(FixedLengthEchoServer.RESPONSE.length()));ch.pipeline().addLast(new FixedLengthClientHandler());}}

3. 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用int32来表示消息的总长度,使用LengthFieldBasedFrameDecoder。

   //客户端跟服务端handle都需要添加如下代码ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));//maxFrameLength: 指定消息的最大长度,超过这个长度的消息会被丢弃。//lengthFieldOffset: 指定长度字段在消息中的偏移量。//lengthFieldLength: 指定长度字段的长度。//lengthAdjustment: 在计算消息长度时,需要添加的偏移量,通常为长度字段本身的长度。//initialBytesToStrip: 解析出一条完整消息后,需要跳过的字节数,通常设置为长度字段的长度。

channelRead和channelReadComplete区别

两者的区别:
       Netty 是在读到完整的业务请求报文后才调用一次业务 ChannelHandler 的 channelRead
方法,无论这条报文底层经过了几次 SocketChannel 的 read 调用。
       但是 channelReadComplete 方法并不是在业务语义上的读取消息完成后被触发的,而是
在每次从 SocketChannel 成功读到消息后,由系统触发,也就是说如果一个业务消息被 TCP
协议栈发送了 N 次,则服务端的 channelReadComplete 方法就会被调用 N 次。

       简单理解就是,消息会被发送到接收缓冲区,如果一条消息很大需要经过两次发送到接收缓冲区才能发完,当收到一个完整的应用层消息报文,channelRead会被触发一次。而ReadComplete方法每次读取完接收缓冲区的报文,channelReadComplete才会被触发一次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/674392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zoho Mail企业邮箱商业扩展第3部分:计算财务状况

在Zoho Mail商业扩展系列的压轴篇章中&#xff0c;王雪琳利用Zoho Mail的集成功能成功地完成了各项工作&#xff0c;并顺利地建立了自己的营销代理机构。让我们快速回顾一下她的成功之路。 一、使用Zoho Mail成功方法概述 首先她通过Zoho Mail为其电子邮件地址设置了自定义域…

spring boot打完jar包后使用命令行启动,提示xxx.jar 中没有主清单属性

在对springBoot接口中间件开发完毕后&#xff0c;本地启动没有任何问题&#xff0c;在使用package命令打包也没异常&#xff0c;打完包后使用命令行&#xff1a;java -jar xxx.jar启动发现报异常&#xff1a;xxx.jar 中没有主清单属性&#xff0c;具体解决方法如下&#xff1a;…

TI毫米波雷达开发——High Accuracy Demo 串口数据接收及TLV协议解析 matlab 源码

TI毫米波雷达开发——串口数据接收及TLV协议解析 matlab 源码 前置基础源代码功能说明功能演示视频文件结构01.bin / 02.binParseData.mread_file_and_plot_object_location.mread_serial_port_and_plot_object_location.m函数解析configureSport(comportSnum)readUartCallback…

day06.C++排序(整理)

一.直接插入排序 void Insertsort(int *a,int n){int i,j;for( i1;i<n;i){if(a[i]<a[i-1]){int tempa[i];//哨兵for( ji-1;temp<a[j];j--){a[j1]a[j];//记录后移}a[j1]temp;//插入到正确位置}} }二.希尔排序 void Shellsort(int *a,int n){for(int dltan/2;dlta>…

(2024 了,这文也太水了)审查 GAN 的 FID 和 SID 指标

Reviewing FID and SID Metrics on Generative Adversarial Networks 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 相关工作 3. 方法 4. 实验 0. 摘要 生成对抗网络&…

如何利用IP定位技术锁定网络攻击者

在当今高度互联的数字世界中&#xff0c;网络安全威胁日益猖獗。为了维护网络空间的安全与稳定&#xff0c;追踪并锁定网络攻击者成为了关键一环。而IP定位技术&#xff0c;作为一种重要的追踪手段&#xff0c;正发挥着越来越重要的作用。 IP定位技术&#xff0c;简而言之&…

Django模板(二)

标签if 标签在渲染过程中提供使用逻辑的方法,比如:if和for 标签被 {% 和 %} 包围,如下所示: 由于在模板中,没有办法通过代码缩进判断代码块,所以控制标签都需要有结束的标签 if判断标签{% if %} {% endif %} : # athlete_list 不为空 {% if athlete_list %}# 输出 ath…

vue实现购物车案例

话不多说&#xff0c;先上效果图。 安装elementui组件库&#xff0c;可直接食用。 <template><div><!-- 购物车部分 --><el-container><el-header><h1>购物车案例一条龙</h1></el-header><el-main><!-- 折叠面板…

【FPGA开发】Modelsim和Vivado的使用

本篇文章包含的内容 一、FPGA工程文件结构二、Modelsim的使用三、Vivado的使用3.1 建立工程3.2 分析 RTL ANALYSIS3.2.1 .xdc约束&#xff08;Constraints&#xff09;文件的产生 3.3 综合 SYNTHESIS3.4 执行 IMPLEMENTATION3.5 烧录程序3.6 程序固化3.6.1 SPI约束3.6.2 .bin文…

特征工程:特征提取和降维-上

目录 一、前言 二、正文 Ⅰ.主成分分析 Ⅱ.核主成分分析 三、结语 一、前言 前面介绍的特征选择方法获得的特征&#xff0c;是从原始数据中抽取出来的&#xff0c;并没有对数据进行变换。而特征提取和降维&#xff0c;则是对原始数据的特征进行相应的数据变换&#xff0c;并…

Rust开发WASM,WASM Runtime运行

安装wasm runtime curl https://wasmtime.dev/install.sh -sSf | bash 查看wasmtime的安装路径 安装target rustup target add wasm32-wasi 创建测试工程 cargo new wasm_wasi_demo 编译工程 cargo build --target wasm32-wasi 运行 wasmtime ./target/wasm32-wasi/d…

猫头虎分享已解决Bug ‍ || TypeError: Object of type ‘int64‘ is not JSON serializable

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Appium使用初体验之参数配置,简单能够运行起来

一、服务器配置 Appium Server配置与Appium Server GUI&#xff08;可视化客户端&#xff09;中的配置对应&#xff0c;尤其是二者如果不在同一台机器上&#xff0c;那么就需要配置Appium Server GUI所在机器的IP&#xff08;Appium Server GUI的HOST也需要配置本机IP&#xf…

spring boot和spring cloud项目中配置文件application和bootstrap加载顺序

在前面的文章基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136060312 日志配置 logback-spring.xml <?xml version"1.0" encoding"UTF-8"?> <configuration scan"true" scanPeriod"10000000 seconds" debug…

探索C语言中的联合体与枚举:数据多面手的完美组合!

​ ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 联合体的定义 联合体又叫共用体&#xff0c;它是一种特殊的数据类型&…

2024智慧城市新纪元:引领未来,重塑都市生活

随着科技的飞速发展和数字化转型的不断深入&#xff0c;2024年智慧城市领域迎来了全新的发展格局。 这一年&#xff0c;智慧城市的建设更加注重人性化、可持续性和创新性&#xff0c;为城市居民带来了前所未有的便捷与舒适。以下将重点关注智慧城市的几个核心内容&#xff0c;…

《幻兽帕鲁》攻略:0基础入门及游戏基础操作 幻兽帕鲁基础设施 幻兽帕鲁基础攻击力 Mac苹果电脑玩幻兽帕鲁 幻兽帕鲁加班加点

今天就跟大家聊聊《幻兽帕鲁》攻略&#xff1a;0基础入门及游戏基础操作。 如果想在苹果电脑玩《幻兽帕鲁》记得安装CrossOver哦。 以下纯干货&#xff1a; CrossOver正版安装包&#xff08;免费试用&#xff09;&#xff1a;https://souurl.cn/Y1gDao 一、基础操作 二、界面…

Logback - 日志框架

引言 在当今的企业级应用开发中&#xff0c;日志管理是一个不可或缺的部分。它不仅帮助我们进行错误跟踪&#xff0c;还能有效监控应用程序的运行状态&#xff0c;为性能优化提供数据支撑。Spring Boot作为一个简化Spring应用开发的框架&#xff0c;自带了强大的日志管理功能。…

雾计算:去中心化计算的未来之旅

雾计算是去中心化计算的基石&#xff0c;它将重塑我们的数字格局。通过使计算和存储更接近数据源&#xff0c;它改变了我们处理物联网生成数据的方式。通过雾计算探索未来&#xff0c;揭示了减少延迟、增强隐私和高效网络利用等好处。 随着传感器和可穿戴设备等物联网设备的数…

PCIe学习笔记(1)Hot-Plug机制

文章目录 Hot-Plug InitHot Add FlowSurprise Remove FlowNPEM Flow Hot-Plug Init PCIe hot-plug是一种支持在不关机情况下从支持的插槽添加或删除设备的功能&#xff0c;PCIe架构定义了一些寄存器以支持原生热插拔。相关寄存器主要分布在Device Capabilities, Slot Capabili…