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.hqwc.cn/news/174859.html

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

相关文章

酷柚易汛ERP - 其他入库单操作指南

1、应用场景 处理其他非采购类型的入库单据&#xff0c;比如赠品、获赔商品、以货抵债、借入、接受捐赠等不参与采购管理的入库类业务。 2、主要操作 2.1 新增其他入库单 打开【仓库】-【其他入库单】&#xff0c;选择商品后&#xff0c;根据存货核算方法自动计算出单位成本…

Java jdbc连接Oracle时出现ORA-28040: No matching authentication protocol报错

一、问题描述 升级了oracle数据库版本后&#xff0c;同时也更新了oracle的驱动为ojdbc8.jar&#xff0c;Java重新通过jdbc连接Oracle时出现ORA-28040: No matching authentication protocol报错。 完整报错信息 java.sql.SQLException: ORA-28040: No matching authenticati…

ARM Linux 基础学习 / 系统相关,文件系统,文件属性

编辑整理 by Staok。 本文部分内容摘自 “100ask imx6ull” 开发板的配套资料&#xff08;如 百问网的《嵌入式Linux应用开发完全手册》&#xff0c;在 百问网 imx6ull pro 开发板 页面 中的《2.1 100ASK_IMX6ULL_PRO&#xff1a;开发板资料》或《2.2 全系列Linux教程&#xf…

我的前端笔记JS

软件 js介绍 js是编程语音&#xff0c;之前学的html和css是标记语言 百度搜索mdn官网就可以 语法 输出、对话框、控制台日志、输入对话框 字面量 简单理解就是看到的内容是属于什么类型&#xff0c;例如1232&#xff0c;这个是属于数字字面量 变量 输入并保存数据 交互两个变…

2023nacos源码解读第3集——nacos-client核心功能之微服务调用和配置管理测试

文章目录 1、测试项目2、项目注意事项3、 测试核心功能3.1 测试服务调用与负载均衡3.2 测试配置监听 4、参考文档 1、测试项目 项目地址 nacos-service-a nacos-service-b 2、项目注意事项 项目初始化可以使用aliyun spring initializer ,以更方便的使用springcloud alibaba…

iceoryx(冰羚)-Service Discovery

Service Discovery Summary and problem description IPC通道&#xff08;例如消息队列或UNIX域套接字&#xff09;上的服务发现是不可执行的&#xff0c;因为传输的数据较大&#xff0c;这可能会导致多个帧的传输。如果发现大量高频服务&#xff0c;例如在启动时&#xff0c…

CSDN每日一题学习训练——Java版(字符串相乘、子集、删除链表的倒数第 N 个结点)

版本说明 当前版本号[20231112]。 版本修改说明20231112初版 目录 文章目录 版本说明目录字符串相乘题目解题思路代码思路补充说明参考代码 子集题目解题思路代码思路参考代码 删除链表的倒数第 N 个结点题目解题思路代码思路参考代码 字符串相乘 题目 给定两个以字符串形…

光刻机ASML CYMER光电模块组件维修114122,S111310

1&#xff1a;436nm g-line 可以满足0.8-0.35 微米制程芯片的生产&#xff0c;对应设备有接触式和接近式光刻机。 2&#xff1a;365nm i-line 同样可以满足0.8~0.35微米制程芯片的生产。设备于上相同。 早期的光刻机采用接触式光刻&#xff0c;即掩模贴在硅片上进行光刻&…

【MongoDB】索引 – 通配符索引

一、准备工作 这里准备一些数据 db.books.drop();db.books.insert({_id: 1, name: "Java", alias: "java 入门", description: "入门图书" }); db.books.insert({_id: 2, name: "C", alias: "c", description: "C 入…

C语言--数组的长度计算【详细解释】

一.数组的长度计算公式 我们都知道字符串有特定的函数strlen,而数组没有&#xff0c;&#xff08;虽然字符串也是一种特殊的数组&#xff09; 但是&#xff0c;类似于这样的数组&#xff1a; int arr[]{12,89,1,5,31,78,45,12,12,0,45,142,21,12}&#xff1b; 我们很难一眼…

数据结构----顺序栈的操作

1.顺序栈的存储结构 typedef int SElemType; typedef int Status; typedef struct{SElemType *top,*base;//定义栈顶和栈底指针int stacksize;//定义栈的容量 }SqStack; 2.初始化栈 Status InitStack(SqStack &S){//初始化一个空栈S.basenew SElemType[MAXSIZE];//为顺序…

腾讯云2核4G服务器CVM标准型S5实例租用5年价格表

腾讯云服务器网整理五年云服务器活动 txyfwq.com/go/txy 配置可选2核4G和4核8G&#xff0c;公网带宽可选1M、3M或5M&#xff0c;系统盘为50G高性能云硬盘&#xff0c;标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器&#xff0c;睿频…