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.rhkb.cn/news/191560.html

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

相关文章

线性代数-Python-05:矩阵的逆+LU分解

文章目录 1 矩阵的逆1.1 求解矩阵的逆 2 初等矩阵2.1 初等矩阵和可逆性 3 矩阵的LU分解3.1 LU分解的实现 1 矩阵的逆 1.1 求解矩阵的逆 def inv(A):if A.row_num() ! A.col_num():return Nonen A.row_num()"""矩阵A单位矩阵"""ls LinearSyste…

相机以及其它传感器传感器

深度相机点云质量对比 比较点云质量时需要注意的点&#xff1a; 1.对特殊材质、颜色的检测效果&#xff1a;透明塑料、金属、毛玻璃、高反光物体&#xff08;镜子、水坑&#xff09;、吸光物体&#xff08;黑色物体&#xff09;。 2.特殊环境&#xff1a;雨、雪、雾、明暗交替位…

【java学习—十四】Class类(2)

文章目录 1. Class类2. Class类的常用方法3. 实例化Class类对象&#xff08;四种方法&#xff09; 1. Class类 在 Object 类中定义了以下的方法&#xff0c;此方法将被所有子类继承&#xff1a; public final Class getClass() 以上的方法返回值的类型是一个 Class 类&#xf…

Revit 平面的圆弧,空间的椭圆弧

大家对Revit的空间曲线那么理解,如何用代码创建空间的椭圆弧,,上看是圆弧,正面看是椭圆? 直接放代码: Document doc = commandData.Application.ActiveUIDocument.Document; Autodesk.Revit.DB.XYZ center = new Autodesk.Revit.DB.XYZ(0, 0, 0); …

%与floormod方法区别

%求余数 计算步骤&#xff1a; 10 / -3 -3.333333........... %是向0方向取整&#xff0c;因此-3.3333.......取整数-3 10 % -3 10-&#xff08;-3*-3&#xff09; 1 floormod方法 计算步骤&#xff1a;floormod(10,-3) floormod是向负无穷方向取整&#xff0c;因此-3…

SpringBoot从零到一项目实战落地博客系统(附源码!!!)

1.项目内容 1.1.页面展示 1.2.博客分类 1.3.面试辅导 1.4.私教带徒 1.5.文章编辑 1.6.后台管理 2.项目架构及技术描述 2.1.本项目用到的技术和框架 项目构建&#xff1a;Mavenweb框架&#xff1a;Springboot数据库ORM&#xff1a;Mybatis数据库连接池&#xff1a; HikariCP分…

【Linux】进程等待

文章目录 tips一、进程等待是什么&#xff1f;二、为什么要有进程等待&#xff1f;三、怎么做到进程等待&#xff1f;先看看什么是进程等待wait和waitpidstatus参数options参数非阻塞轮询 进程等待的原理 总结 tips 下面的代码可以循环检测进程。 while :; do ps ajx | head …

Git推送本地代码到远程仓库

Git推送本地代码到远程仓库 1、首先需要安装Git&#xff0c;如果已经安装&#xff0c;请跳过。下载地址&#xff1a;https://git-for-windows.github.io/ 2、安装好git服务器后。首先找到你项目的文件夹&#xff0c;比如项目名称为Item&#xff0c;进入到这个文件夹&#xff0…

v-bind和v-model

目录 前言 v-bind 作用 语法格式 编译原理 简写 v-model 作用 使用方法 v-bind和v-model的区别和联系 前言 本文我们来了解一下模板语法之指令语法中的v-bind和v-model v-bind 作用 v-bind可以让html标签的某个属性的值产生动态的效果 语法格式 <html标签 v-bin…

企业级真实应用利用Mybatis-Plus进行分页查询处理

怎么导入依赖我在之前的文章里边有说过不理解的可以看看 你应该懂点Mybatis-plus&#xff0c;真的好用 1&#xff1a;了解Page<T>类的使用 首先我们需要使用到Page类 &#xff0c;建立一个Page类&#xff0c;泛式类型中放入我们需要输出的类&#xff0c;是列表的话就…

2023年亚太杯数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 赛题思路算法介绍FP树表示法构建FP树实现代码 建模资料 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#…

任意注册漏洞

目录 一漏洞介绍 二实战演示 三漏洞修复 本文由掌控安全学院 - 小博 投稿 一漏洞介绍 1.未验证邮箱/手机号 情景&#xff1a;应用为了方便用户记录用户名&#xff0c;使用邮箱和手机号作为用户名&#xff08;因此很多应用在注册的时候就要求用户填写&#xff0c;多数时候…

安卓:打包apk时出现Execution failed for task ‘:app:lintVitalRelease

Execution failed for task :lintVitalRelease 程序可以正常运行&#xff0c;但是打包apk的时候报Execution failed for task ‘:app:lintVitalRelease导致打包失败&#xff0c;原因是执行lintVitalRelease失败了&#xff0c;存在错误。解决办法&#xff1a;在app模块的build.…

万宾科技内涝积水监测仪效果,预警城市积水

当城市之中出现强降雨或者大暴雨&#xff0c;可能会导致雨水不断堆积到城市排水管网之中&#xff0c;可能还会淹没城市的排水系统时&#xff0c;这种现象被称为城市之中的内涝&#xff0c;并且在许多城市之中内涝问题日益引起人们的关注。 内涝积水监测仪的出现成为了希望的灯塔…

Android Studio的代码笔记--JSON解析学习2

JSON学习2 生成JSON解析JSON java解析json字符串和合成json字符串 json字符串 {"type":"getConfig","ip":"192.168.1.100"}使用 String ss groupJS("Config","192.168.1.100"); splitJS(ss);回显 I/lxh: group…

arcgis--NoData数据处理

方法一&#xff1a;利用【栅格计算器】可以对NoData的值进行修改。【Spatial Analyst工具】-【地图代数】-【栅格计算器】&#xff0c;将NoData修改为某一个值。 方法二&#xff1a;先对原始数据进行重分类&#xff0c;分成1类&#xff0c;将NoData赋值为2,。然后&#xff0c;将…

2023.11.11通过html内置“required-star“添加一个红色的星号来表示必填项

2023.11.11通过html内置"required-star"添加一个红色的星号来表示必填项 在HTML中&#xff0c;可以使用标签来为元素添加说明。同时可以通过添加一个红色的星号来表示必填项。 <!DOCTYPE html> <html lang"en"> <head><meta charse…

GoLong的学习之路,进阶,语法之并发(并发错误处理)补充并发三部曲

这篇文章主要讲的是如何去处理并发的错误。 在Go语言中十分便捷地开启goroutine去并发地执行任务&#xff0c;但是如何有效的处理并发过程中的错误则是一个很棘手的问题。 文章目录 recovererrgroup recover 哦对&#xff0c;似乎没写错误处理的文章。后面补上。 首先&…

一文懂得电源模块过温保护测试方法 ate测试软件助力测试

过温保护测试是电源模块保护功能测试项目之一&#xff0c;也是电源模块测试的重要测试指标&#xff0c;以保证电源模块过温保护功能正常&#xff0c;确保电源模块不受损坏。用ate测试软件测试电源模块过温保护&#xff0c;不仅可以保证测试结果的准确性&#xff0c;还可以多维度…

[Linux] dns域名解析服务

一、DNS 1.1 DNS简介 域名解析&#xff1a;&#xff08;英文&#xff1a;Domain Name System&#xff0c;缩写&#xff1a;DNS&#xff09;是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS使用udp53和tcp53…