Netty入门指南之NIO 粘包与半包

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Netty应用专栏_Aomsir的博客-CSDN博客

文章目录

  • 参考文献
  • 前言
  • 问题产生实际场景
  • 问题出现
  • 问题解决
  • 总结

参考文献

  • 孙哥suns说Netty
  • Netty官方文档

前言

在之前的文章中,我们深入了解了NIO中的两个核心模块,ChannelBuffer,包括它们的结构、作用以及所解决的问题等。然而,虽然我们已经掌握了理论知识,但尚未经历实际的应用。在今天的这篇文章中,我们将以实战场景为例,探讨如何使用Channel和Buffer来解决一个常见的问题,即半包与粘包

问题产生实际场景

让我们考虑一个实际场景:客户端与服务端建立了连接,客户端需要向服务端发送三个句子:I'm AomsirI love youDo you love me?。然而,由于计算机无法理解文本的含义,它在接收这些句子时并不知道何时结束每句话。为了解决这个问题,我们通常在每个句子的末尾添加换行符\n。这样,在解析数据时,服务端可以根据换行符来确定每个句子的结束。实际上,这也是网络通信中协议概念。
在这里插入图片描述

问题出现

在上面的场景中,我们假设了一个常见的情况,其中客户端和服务端之间使用NIO中的Channel进行通信。服务端将从Channel中读取的数据放入ByteBuffer中。然而,在确定Buffer的大小时,我们面临一个挑战:

设置一个过大的Buffer可能会导致资源浪费,而设置一个过小的Buffer则可能导致半包和粘包问题。

半包和粘包是通信中常见的问题,通常在数据读取和解析过程中引发。举例来说,如果我们将Buffer大小设置为15,并且客户端发送的第一句话(包括换行符)只有12个字符,没有超过15,那么第二句话会被读入Buffer。但是第二句话只读取了Buffer的前几个字符,然后Buffer就满了。此时,Buffer中包含第一句完整和第二句的开头,这就是粘包。接着,Buffer继续从Channel中读取第二句的剩余部分和第三句的开头,这导致Buffer中包含第二句的结尾和第三句的开头,这就是半包。半包和粘包问题可能会导致我们在处理接收到的数据时遇到一些困难
在这里插入图片描述

问题解决

显然,我们不能允许我们的程序出现半包和粘包问题,因此我们需要采取措施来解决这个问题。我们可以借助ByteBuffer的compact方法来解决这一挑战。解决思路是在每个句子的末尾添加换行符\n的基础上,遍历原始Buffer,在遇到\n时将其之前的数据通过循环方式放入名为target的Buffer中,然后进行输出。如果原始Buffer中只有一个\n,后续的循环将不会进入if条件,最终将剩余的部分压缩到原始Buffer的最开始,以便继续接收数据。

需要注意的是,为了避免原始Buffer中出现两个\n(即两个完整的句子),target的Buffer大小不能随意设置。我们可以使用i + 1 - buffer.position来确定target的长度,因为在ByteBuffer.get(i)的过程中,position不会移动,只有在ByteBuffer.get()时才会使position不断前进,所以我们就可以在程序中动态的计算长度(也就是 position - i)之间的长度。

还有一个需要注意的问题是,如果原始Buffer中没有\n,整个程序可能会陷入死循环。为了解决这种情况,我们可以在else部分采取适当的处理措施。然而,这个具体处理方法超出了本文的范围,因为后续的Netty框架已经为我们提供了解决半包和粘包问题的更全面的解决方案

public class TestNIO10 {public static void main(String[] args) {ByteBuffer buffer = ByteBuffer.allocate(50);// 假装buffer从channel里面读取到了第一次数据buffer.put("Hi Aomsir\n I love y".getBytes());doLineSplit(buffer);// 假装buffer从channel里面读取到了第二次数据buffer.put("ou\nDo you like me?\n".getBytes());doLineSplit(buffer);}private static void doLineSplit(ByteBuffer buffer) {// 读模式,让程序从buffer里面读取数据buffer.flip();// 循环会将整个buffer的数据都读取一遍for (int i = 0; i < buffer.limit(); i++) {// 在找到一行完整数据以后没有直接结束循环是因为可能会出现两个\n的情况if (buffer.get(i) == '\n') {// 以免出现一行里面有多个\n的情况// 注意:get(i)不会导致position的变化int length = i + 1 - buffer.position();// buffer的大小不能写死,每个句子的大小不一样,所以要动态分配ByteBuffer target = ByteBuffer.allocate(length);// 从buffer里面读取数据写入targetfor (int j = 0; j < length; j++) {target.put(buffer.get());}// 截取工作完成,将target切换为读模式,然后读取数据target.flip();System.out.println("StandardCharsets.UTF_8.decode(target) = " + StandardCharsets.UTF_8.decode(target));target.clear();}}// buffer切换写模式,将未读完的数据移到最前面(position-limit之间)buffer.compact();}
}

总结

今天的文章介绍和解决了半包和粘包的区别,这部分需要对Channel和Buffer的读写有一定的基础,如果没有看明白就先看看前面的文章打好基础,为本篇和以后的文章打基础。

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

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

相关文章

深度学习读取txt训练数据绘制参数曲线图的方法

有一些深度学习模型是并不像yolo系列那样最终输出相应的参数图&#xff0c;有很多训练形成了一个训练log文件&#xff0c;于是需要读取log文件中的内容并绘制成曲线图。 如下实例&#xff0c;有一个log文件的部分截图&#xff0c;需要将其读取出来并绘制曲线图 废话不多说&…

前端之Bootstrap框架

目录 【一】Bootstrap介绍 【二】Bootstrap引入 【1】CDN加速链接 【2】注意 【三】布局容器 【四】栅格系统 【五】栅格参数 【六】列偏移 【七】排版 标题 内联文本元素 对齐 改变大小写 引用 列表 【八】表格 基本实例 条纹状表格 带边框的表格 鼠标悬停…

汽车工业生产线数字孪生可视化管理平台,赋予工厂车间数字化智慧化管理

在工业4.0 的时代背景下&#xff0c;随着企业数字化进程的推进&#xff0c;数字孪生可视化技术逐渐在汽车行业得到广泛应用&#xff0c;数字孪生智慧工厂的建设也成为了汽车行业数字化转型的趋势之一。汽车制造业属于典型的离散制造行业&#xff0c;汽车生产包含冲压、焊接、涂…

19.13 Boost Asio 发送TCP流数据

Boost框架中默认就提供了针对TCP流传输的支持&#xff0c;该功能可以用来进行基于文本协议的通信&#xff0c;也可以用来实现自定义的协议。一般tcp::iostream会阻塞当前线程&#xff0c;直到IO操作完成。 首先来看服务端代码&#xff0c;如下所示在代码中首先通过GetFileSize…

C++的Odyssey之旅——STL

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; &#x1f354;前言&#xff1a;我们已经将基本语法了解的差不多了&#xff0c;现在我们就该进入C中最重要也是最富有特点的一部分——STL。在学习C语言中我们想要使用顺序表、链表等一些数据结构进行做题时都需要进行这…

Go invalid memory address or nil pointer dereference错误 空指针问题

Go 指针声明后赋值&#xff0c;出现 panic: runtime error: invalid memory address or nil pointer dereference&#xff0c;这种是内存地址错误。 首先我们要了解指针&#xff0c;指针地址在 Go 中 * 代表取指针地址中存的值&#xff0c;& 代表取一个值的地址对于指针&am…

WordPress主题 JustNews主题6.0.1(亲测首页不空白)

介绍 资源入口 需要用WordPress5.X版本 JustNews介绍&#xff1a;一款专为博客、自媒体、资讯类的网站设计开发的WordPress主题&#xff0c;自v3.0版开始支持自主研发的前端用户中心&#xff0c;不仅支持注册、登录、账户设置、个人中心等常用页面的添加&#xff0c;还可以上传…

【原创】java+jsp+servlet简单图书管理系统设计与实现

摘要&#xff1a; 图书管理系统是一个专门针对图书馆管理而设计的系统&#xff0c;它可以帮助图书管理员有效的对图书进行管理&#xff0c;在图书管理系统的设计中&#xff0c;首先要考虑的是系统的需求分析&#xff0c;该系统的设计与实现涉及多个方面&#xff0c;包括数据库…

【赠书第2期】嵌入式虚拟化技术与应用

文章目录 前言 1 背景概述 2 专家推荐 3 本书适合谁&#xff1f; 4 内容简介 5 书籍目录 6 权威作者团队 7 粉丝福利 前言 随着物联网设备的爆炸式增长和万物互联应用的快速发展&#xff0c;虚拟化技术在嵌入式系统上受到了业界越来越多的关注、重视和实际应用。嵌入式…

Linux 入门

Linux 入门 1&#xff1a;linux 用户 root 用户 &#xff1a;也叫超级用户&#xff0c;UID0&#xff0c;其权限最高。系统用户&#xff1a;也叫虚拟用户&#xff0c;UID 1-999普通用户: UID1000-60000, 可以登录系统,操作自己目录下的文件. 1.1:用户操作命令 切换用户: su …

ts面试题总结

文章目录 前言ts和js的区别&#xff1f;什么是Typescript的方法重载&#xff1f;Typescript中never 和 void 的区别&#xff1f;typescript 中的 is 关键字有什么用&#xff1f;TypeScript支持的访问修饰符有哪些&#xff1f;如何定义一个数组&#xff0c;它的元素可能是字符串…

【Mybatis小白从0到90%精讲】12:Mybatis删除 delete, 推荐使用主键删除!

文章目录 前言XML映射文件方式推荐使用主键删除注解方式工具类前言 在实际开发中,我们经常需要删除数据库中的数据,MyBatis可以使用XML映射文件或注解来编写删除(delete)语句,下面是两种方法的示例。 XML映射文件方式 Mapper: int delete(int id);Mapper.xml:

U-Mail信创邮件系统解决方案

近年来&#xff0c;在国家政策的大力引导和自身数字化转型需求驱动下&#xff0c;国产化成为国内数字化发展道路上的关键词&#xff0c;企业不断加强自主创新能力&#xff0c;进行信创建设&#xff0c;实现软硬件系统国产化替代&#xff0c;已成为大势所趋。邮件系统作为企业管…

YOLO目标检测数据集大全【含voc(xml)、coco(json)和yolo(txt)三种格式标签+划分脚本+训练教程】(持续更新建议收藏)

一、作者介绍&#xff1a;资深图像算法工程师&#xff0c;YOLO算法专业玩家&#xff1b;擅长目标检测、语义分割、OCR等。 二、数据集介绍&#xff1a; 真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;分享的绝大部分数据集已应用于各种实际落地项目。所有数据…

MarkdownPad2, CSDN及有道云笔记对数学公式的支持

MarkdownPad2, CSDN及有道云笔记对数学公式的支持 MarkdownPad2的安装 下载并安装MrakdownPad2软件&#xff0c;下载地址安装awesomium_v1.6.6_sdk_win&#xff0c; 下载地址安装支持公式编辑的插件&#xff0c;注意&#xff0c;在MarkdownPad2的 Tools > Options > Ad…

Linux提权方法总结

1、内核漏洞提权 利用内核漏洞提取一般三个环节&#xff1a;首先对目标系统进行信息收集&#xff0c;获取系统内核信息及版本信息 第二步&#xff0c;根据内核版本获取对应的漏洞以及exp 第三步&#xff0c;使用exp对目标进行攻击&#xff0c;完成提权 注&#xff1a;此处可…

景联文科技提供高质量人像采集服务,助力3D虚拟人提升逼真度

人像采集是一种通过特定设备或技术&#xff0c;对人的相貌、身材等特征信息进行收集和处理的过程&#xff0c;可应用于3D虚拟人领域。通过采集大量的人像数据&#xff0c;可以训练和优化人像识别算法&#xff0c;提高其准确性。 人像采集对于提高3D虚拟人的逼真度、个性化定制以…

flink的带状态的RichFlatMapFunction函数使用

背景 使用RichFlatMapFunction可以带状态来决定如何对数据流进行转换&#xff0c;而且这种用法非常常见&#xff0c;根据之前遇到过的某个key的状态来决定再次遇到同样的key时要如何进行数据转换&#xff0c;本文就来简单举个例子说明下RichFlatMapFunction的使用方法 RichFl…

经典矩阵试题(一)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、回型矩阵1、题目介绍2、思路讲解3、代码实现4、结果 二、蛇型矩阵1、题目介绍2、思路讲解…

[设计模式] 建造者模式

一、引言 起因是学习okhttp过程中遇到的这段代码 Request request original.newBuilder().url(original.url()).header("Authorization", "Bearer " BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret())).header(&quo…