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

news2024/12/28 18:40:32

作者简介:☕️大家好,我是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里面读取数据写入target
                for (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.coloradmin.cn/o/1189328.html

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

相关文章

电脑篇——本地串口转TCP,TCP转虚拟串口,网络调试助手,串口调试助手

TCP/UDP工具、串口工具 https://pan.baidu.com/s/1SY03d_RRVhyOZfsPlApmxg?pwd5555 今日有个需求&#xff0c;就是在本机电脑上接了一个串口设备&#xff0c;然后我的QtCreator是在内网远程电脑运行的&#xff0c;我想将串口设备“挂载”到远程电脑上去调试程序&#xff0c;于…

【Mysql】增删改查(基础版)

我使用的工具是Data Grip &#xff08;SQLyog Naivact 都行&#xff09; 使用Data Grip创建student表&#xff0c;具体步骤如下&#xff08;熟悉Data Grip或者使用SQLyog&#xff0c;Naivact可以跳过&#xff09; https://blog.csdn.net/m0_67930426/article/details/13429…

金蝶云星空下游单据的操作控制上游单据的状态转换开发方案

文章目录 金蝶云星空下游单据的操作控制上游单据的状态转换开发方案说明方案设计开发实现售后单增加变更状态反写规则反写状态&#xff1a;在保存配置了反写状态保存后删除&#xff0c;反写规则不生效&#xff0c;需要在删除操作配置插件根据关联关系进行反写生效操作&#xff…

如何实现云端开发能力快速提升?【DevRun】云上开发创新实践带你实现

随着企业数字化的转型趋势&#xff0c;软件成为数字化转型的关键驱动力&#xff0c;在云计算越来越普及且作用愈发重要的今天&#xff0c;现代应用正以难以想象的速度在增长&#xff0c;同时对软件开发工具提出了新的要求。 华为云CodeArts作为一站式云上开发创新工具&#xf…

从零开始:PRD产品需求文档怎么写

产品需求文档&#xff08;PRD&#xff09;是所有产品开发项目的关键环节&#xff0c;它的主要职责是传递关于即将开发的产品的所有核心信息给团队。作为一名Pixso设计师&#xff0c;我们清楚这个过程可能显得复杂和耗费精力&#xff0c;但这并不意味着我们不能让它变得更为简洁…

国产猫罐头可以长期作为主食吗?我家的优质TOP的猫罐头分享

我最近一直在调查国产猫罐头可以长期作为主食吗&#xff1f;看看我的购物订单&#xff0c;我已经尝试了几十款了。今天&#xff0c;我想和大家分享一些关于国产猫罐头的经验和见解。 近年来&#xff0c;国产宠粮市场取得了突破性的进展&#xff0c;各个猫粮商在配方、营养数据…

pinpoint监控tomcat应用,页面显示No data collected

pinpoint安装部署教程大家都可以搜到。这里就不说了。单说一下 页面没有数据的情况。 部署环境&#xff0c;pinpoint安装部署在A服务器上。现在是在C、D、E、F……linux机器上安装pinpoint-agnet 1. 将文件 pinpoint-agent-1.8.5.tar.gz 上传到 服务器C、D、E、F…… 2. 解压…

解决IDEA使用卡顿的问题

*问题&#xff1a;使用IDEA的时候卡顿 原因&#xff1a;IDEA默认分配的内存有上限 **可以查看内存分配情况及使用情况__ 解决&#xff1a; 设置JVM的启动参数&#xff1a; 进入idea的安装目录的bin文件夹 -Xms1024m -Xmx2048m -XX:ReservedCodeCacheSize1024m -XX:UseG1G…

洋子带你赚钱,粉丝有奖任务来啦,最高拿90京东卡

大家好&#xff0c;我是洋子&#xff0c;前段时间CSDN联合阿里云发布了免费试用3种 云服务器的活动任务&#xff0c;每完成一个任务就可以拿到30京东卡&#xff0c;3个任务互相独立&#xff0c;如果3个任务全部完成就可以拿到90京东卡 任务奖励 参与体验大概十几分钟&#xf…

JS点击图片指定对象变色两种方法

要求&#xff1a;点击上面的颜色实现下面的图像变成相同的颜色 难点&#xff1a;对于js函数的this对象不太清楚如何传递 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>changeColor</title>&l…

Redis注解式开发并整合ssm项目以及击穿,穿透和雪崩的介绍及解决方案

目录 一. Redis整合ssm项目 1.1 pom配置 1.2 spring-redis.xml ①注册一个redis.properties ②配置数据源 ③连接工厂 ④配置序列化器 ⑤配置redis的key生成策略 1.3 applicationContext.xml 中添加spring-redis.xml 二. Redis的注解式开发 2.1 Cacheable 2.2 CachePut …

leetcode刷题日记:100.Same Tree(相同的树)和101.Symmetric Tree(对称二叉树)

100.Same Tree(相同的树) 题目给了我们两棵树要我们判断这两颗树是否相同&#xff0c;我首先想到的就是前序序列与中序序列可以唯一确定一棵树&#xff0c;如果我能分别确定这两棵树的前序序列与中序序列&#xff0c;然后再分别比较它们的前序序列与中序序列就能得到这两棵树是…

猫罐头怎么选?千万别错过这5款好吃放心的猫罐头推荐!

猫罐头不仅美味可口&#xff0c;而且营养丰富&#xff0c;是专为猫咪打造的美食。那么&#xff0c;猫罐头怎么选&#xff1f;作为一位经营宠物店7年的店长&#xff0c;我对猫猫的饮食都非常重视&#xff0c;也见证了很多猫咪品尝各种猫罐头的瞬间&#xff0c;现在我对各个品牌的…

STK 根据六根数文件导出星下点(二)

利用给定的六根数数据&#xff0c;生成星下点数据 首先在STK中建立卫星对象 建立成功后&#xff0c;在Satellite1中设置Properties ,输入六根数数据 使用如下参数&#xff1a; propagator&#xff08;轨道&#xff09;&#xff1a;TwoBody&#xff08;开普勒&#xff09; st…

【数据结构】链表经典OJ题,常见几类题型(一)

目录 题型一&#xff1a;反转单链表思路解析OJ题实例解题代码 题型二&#xff1a;快慢指针思路解析OJ题实例解题代码 两类题型的结合 题型一&#xff1a;反转单链表 思路解析 反转一个链表主要是想让第一个节点指向NULL&#xff0c;第二个节点指向第一个&#xff0c;以此类推。…

键盘win键无法使用,win+r不生效、win键没反应、Windows键失灵解决方案(亲测可以解决)

最近几天发现自己笔记本的win键无法使用&#xff0c;win失灵了&#xff0c;但是外接键盘后则正常:。 这个问题困扰了我一周&#xff0c;我都以为自己的枪神坏了。 寻找了几个解决方法&#xff0c;网上看了好多好多稀里糊涂的办法&#xff0c;都是不管用的&#xff0c;这里给大…

onvif backchannel

Back Channel笔记_onvif backchannel-CSDN博客 backchannel 由客户端触发&#xff1a;

007 Linux fork()函数

前言 本文将会以提问的形式展开向你介绍fork函数 文章重点 关于fork函数&#xff0c;本文重点在于解决以下疑问 疑问一&#xff1a; 为什么fork之前的代码只有父进程执行&#xff0c;然而fork之后的代码父子进程都要执行 疑问二&#xff1a; 1、既然fork之后父子进程会执行一…

【微服务】API治理发展历史与未来趋势

目录 一、前言 二、API治理的价值和意义 2.1 API治理概念 2.2 API治理价值和意义 2.2.1 提升团队协同效率 2.2.2 降低产品运维成本 2.2.3 识别和降低系统的外部风险 2.2.4 提供更多的拓展性 三、API生命周期管理 ​编辑 3.1 规划阶段 3.2 开发阶段 3.3 测试阶段 3…

【Linux网络】网卡配置与修改主机名,做好基础系统配置

目录 一、网络配置命令 1、查看网卡信息ifconfig Linux永久修改ip地址 2、主机名修改 ①hostname 临时修改主机名 ②永久修改主机名 第一种&#xff0c;使用命令修改 第二种&#xff1a;修改配置文件 3、路由信息 再来拓展一下&#xff0c;永久修改路由表信息 4、检查…