Netty笔记9:粘包半包

news2025/3/5 23:26:42

Netty笔记1:线程模型

Netty笔记2:零拷贝

Netty笔记3:NIO编程

Netty笔记4:Epoll

Netty笔记5:Netty开发实例

Netty笔记6:Netty组件

Netty笔记7:ChannelPromise通知处理

Netty笔记8:ByteBuf使用介绍

Netty笔记9:粘包半包

Netty笔记10:LengthFieldBasedFrameDecoder

Netty笔记11:编解码器

Netty笔记12:模拟Web服务器

Netty笔记13:序列化

文章目录

  • 前言
  • 什么是粘包/半包
  • 那如何解决粘包,半包?

前言

粘包/半包是一个很重要的概念,在网络数据传输中一定存在的问题,我们需要理解和会解决,以及运用Netty提供的工具解决。

什么是粘包/半包

粘包:在客户端和服务端之间,会维持一个连接,可以发送多个数据包,但是如果发送的网络数据包太小,那么它会启用Nagle算法,对较小的数据包进行合并,待超时或者数据包大小够大时再发送,也是因为这个原因,TCP的网络延迟会比UDP高一些,服务端对于这样合并发送的消息就无法区分哪些数据是分开发送的,也就是粘包;服务器再接收到数据后,放到缓冲区中,如果消息没有被及时从缓冲区取走,下次再取数据的时候可能会一次取出多个数据包的情况,产生粘包现象;

之前提到UDP,它本身作为无连接的不可靠传输协议,适合频繁发送数据较小的数据包,它不会对数据包进行合并发送,也就是发送什么,就接收到什么。

拆包:服务器将粘粘在一起的数据包进行拆分;

半包/分包:网络传输的套接字的发送缓冲区不够,或者达到了TCP最大报文长度(MSS)大小,导致接收到的数据不完整,还需要接收剩余的数据;

下面模拟粘包场景

修改clienthandler,尝试在客户端建立连接时发送100个数据包给服务端

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 连接成功后激活这个方法
        for (int i = 0; i < 100; i++) {
            // 这里消息,再次用分隔符进行分割保证100个数据都是独立的数据包
            ctx.writeAndFlush(Unpooled.copiedBuffer(("我已经连上了"+System.getProperty("line.separator")).getBytes(CharsetUtil.UTF_8)));
        }
    }

之后在服务端接收数据,并统计接收次数

public class ServerHandler extends ChannelInboundHandlerAdapter {
    static int count = 0;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buffer = (ByteBuf)msg;
        System.out.println("接收到数据:" + buffer.toString(CharsetUtil.UTF_8));
        System.out.println("记录次数:" + ++count);
        System.out.println("ok--------------------------------");
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }
}

结果如下,服务端对100个的接收,其实只是读取了一次,甚至于我们增加了分隔符,也还是把这100个数据包,合并成一个发送了过来

image-20240613215519695

TCP最大数据长度就等于65535-20(20是固定的IP头部)=65495,一个http能发送的最大报文长度受诸多影响,如MSS,网络环境,应用层协议等,所以,理论上是小于65495这个值的。

会出现粘包,半包,是 由TCP传输层协议决定的,因为TCP不知道传输的数据多少字节是一个包,所以,这个现象是由应用层解决的。

那如何解决粘包,半包?

方法一:TCP不知道多少字节算一个包,所以我们可以添加分隔符,而这个分隔符是需要双端都要统一的,不然一端以回车换行为分隔符,另一端以空格为分隔符,就会造成数据分包不对;

方法二:指定数据长度,需要双端统一,需要在传输的字节中,添加数据包长度,在另一端读取解码时就可以知道读取多少为一个数据包;

方法一示例:

那我们定义一个回车换行符为分隔符,并添加到channelInitializer中;

注意:我这里以System.getProperty("line.separator")为分隔符,这个变量取自计算机操作系统,win和linux他们的值不同,所以,需要明确这个值,我这里是偷懒了。

如果说是回车换行为分隔符的解码器,Netty已经提供了一个LineBasedFrameDecoder

 ByteBuf byteBuf = Unpooled.wrappedBuffer(System.getProperty("line.separator").getBytes(StandardCharsets.UTF_8));
                        socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));

image-20240613222114141

来看结果:

image-20240613222137004

方法二示例:

指定数据长度在数据包中,并解码,这个在Netty中是有实现的:

LengthFieldPrepender:经过这个过滤器会在发送的消息前增加一个长度字段,一般使用只有一个参数的构造器,指定长度的大小,这个长度不会改变整个消息的长度;如果要将长度累计到消息长度上,使用new LengthFieldPrepender(4,true)

一般是配合LengthFieldBasedFrameDecoder使用。

例如:

bootstrap.group(group, work)
                     // server指定为ServerChannel
                     .channel(NioServerSocketChannel.class)
                     // 绑定端口
                     .localAddress(8080)
                     // 事件处理器
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel socketChannel) throws Exception {
                             socketChannel.pipeline()
                                          // 数据写出时,增加一个4字节长度的字段
                                          .addLast(new LengthFieldPrepender(4))
                                          .addLast(new LengthFieldBasedFrameDecoder(65535, 0, 4, 0, 4));

关于LengthFieldBasedFrameDecoder的详细说明,在下一篇。

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

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

相关文章

LabVIEW虚拟弗兰克赫兹实验仪

随着信息技术的飞速发展&#xff0c;虚拟仿真技术已经成为教学和研究中不可或缺的工具。开发了一种基于LabVIEW平台开发的虚拟弗兰克赫兹实验仪&#xff0c;该系统不仅能模拟实验操作&#xff0c;还能实时绘制数据图形&#xff0c;极大地丰富了物理实验的教学内容和方式。 ​ …

spring boot + vue 搭建环境

参考文档&#xff1a;https://blog.csdn.net/weixin_44215249/article/details/117376417?fromshareblogdetail&sharetypeblogdetail&sharerId117376417&sharereferPC&sharesourceqxpapt&sharefromfrom_link. spring boot vue 搭建环境 一、浏览器二、jd…

清华团队提出HistoCell,从组织学图像推断超分辨率细胞空间分布助力癌症研究|顶刊精析·25-03-02

小罗碎碎念 今天和大家分享一篇2025-02-21发表于nature communications的文章&#xff0c;内容涉及病理空转单细胞。 从组织学图像推断细胞空间分布对癌症研究意义重大&#xff0c;但现有方法存在标注工作量大、分辨率或特征挖掘不足等局限。研究旨在开发一种高效准确的方法。 …

html+js 轮播图

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>轮播图示例</title><style>/* 基本样式…

vue3:初学 vue-router 路由配置

承上一篇&#xff1a;nodejs&#xff1a;express js-mdict 作为后端&#xff0c;vue 3 vite 作为前端&#xff0c;在线查询英汉词典 安装 cnpm install vue-router -S 现在讲一讲 vue3&#xff1a;vue-router 路由配置 cd \js\mydict-web\src mkdir router cd router 我还…

Python 爬取唐诗宋词三百首

你可以使用 requests 和 BeautifulSoup 来爬取《唐诗三百首》和《宋词三百首》的数据。以下是一个基本的 Python 爬虫示例&#xff0c;它从 中华诗词网 或类似的网站获取数据并保存为 JSON 文件。 import requests from bs4 import BeautifulSoup import json import time# 爬取…

C语言408考研先行课第一课:数据类型

由于408要考数据结构……会有算法题…… 所以&#xff0c;需要C语言来进行一个预备…… 因为大一贪玩&#xff0c;C语言根本没学进去……谁能想到考研还用得到呢&#xff1f;【手动doge&#xff08;bushi&#xff09; 软件用的是Clion&#xff0c;可以自行搜索教程下载使用。…

03 HarmonyOS Next仪表盘案例详解(二):进阶篇

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 文章目录 前言1. 响应式设计1.1 屏幕适配1.2 弹性布局 2. 数据展示与交互2.1 数据卡片渲染2.2 图表区域 3. 事件处理机制3.1 点击事件处理3.2 手势…

探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(一)

文章目录 2.3 Polar 编解码2.3.1 Polar 码简介与发展背景2.3.2 信道极化理论基础对称容量与巴氏参数对称容量 I ( W ) I(W) I(W)巴氏参数 Z ( W ) Z(W) Z(W)常见信道信道联合信道分裂信道极化 本博客为系列博客&#xff0c;主要讲解各基带算法的原理与应用&#xff0c;包括&…

基础篇(一)强化学习是什么?从零开始理解智能体的学习过程

强化学习是什么&#xff1f;从零开始理解智能体的学习过程 你是否曾好奇过&#xff0c;人工智能是如何在复杂的环境中学会做出决策的&#xff1f;无论是打游戏的AI&#xff0c;还是自动驾驶的汽车&#xff0c;还是最近很火的DeepSeek它们的背后都离不开一种强大的技术——强化…

如何直接导出某个conda环境中的包, 然后直接用 pip install -r requirements.txt 在新环境中安装

1. 导出 Conda 环境配置 conda list --export > conda_requirements.txt这将生成一个 conda_requirements.txt 文件&#xff0c;其中包含当前环境中所有包的列表及其版本信息。 2. 转换为 requirements.txt 文件 grep -v "^#" conda_requirements.txt | cut -d …

基于 HTML、CSS 和 JavaScript 的智能九宫格图片分割系统

目录 1 前言 2 技术实现 2.1 HTML 结构 2.2 CSS 样式 2.3 JavaScript 交互 3 代码解析 3.1 HTML 部分 3.2 CSS 部分 3.3 JavaScript 部分 4 完整代码 5 运行结果 6 总结 6.1 系统特点 6.2 使用方法 1 前言 在当今数字化的时代&#xff0c;图片处理需求日益增长。…

委托者模式(掌握设计模式的核心之一)

目录 问题&#xff1a; 举例&#xff1a; 总结&#xff1a;核心就是利用Java中的多态来完成注入。 问题&#xff1a; 今天刷面经&#xff0c;刷到装饰者模式&#xff0c;又进阶的发现委托者模式&#xff0c;发现还是不理解&#xff0c;特此记录。 举例&#xff1a; ​老板​…

MySQL-高级查询

查询处理 排序&#xff08;默认不是按主键排序的&#xff09; order by 字段1[&#xff0c;字段2] [asc|desc] 默认是升序排序也可以指定 select 列表中列的序号进行排序如果是多个字段&#xff0c;那么在上一个字段排序完的基础上排序下一个 限制数量 limit 行数&#xff0…

Apache Kafka单节点极速部署指南:10分钟搭建开发单节点环境

Apache Kafka单节点极速部署指南&#xff1a;10分钟搭建开发单节点环境 Kafka简介&#xff1a; Apache Kafka是由LinkedIn开发并捐赠给Apache基金会的分布式流处理平台&#xff0c;现已成为实时数据管道和流应用领域的行业标准。它基于高吞吐、低延迟的设计理念&#xff0c;能够…

Redis7——进阶篇(一)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…

Sourcetrail 代码分析工具

Sourcetrail 概述 Sourcetrail 是一个代码分析工具&#xff0c;它旨在帮助开发人员理解和导航复杂的代码库。它可以创建代码库的可视化图形&#xff0c;显示代码中的类、函数、变量、依赖关系等信息&#xff0c;从而帮助开发人员更好地理解代码结构和关系&#xff0c;降低维护…

从数据到决策,永洪科技助力良信电器“智”领未来

在数字经济浪潮汹涌的时代&#xff0c;数字化转型已成为企业增强竞争力、实现可持续发展的必由之路。良信电器&#xff0c;作为国内知名的电气设备制造企业&#xff0c;积极响应时代号召&#xff0c;携手永洪科技&#xff0c;共同开启了数字化转型的新篇章。 上海良信电器股份有…

Python-04BeautifulSoup网络爬虫

2025-03-04-BeautifulSoup网络爬虫 记录BeautifulSoup网络爬虫的核心知识点 文章目录 2025-03-04-BeautifulSoup网络爬虫 [toc]1-参考网址2-学习要点3-核心知识点1. 安装2. 导入必要的库3. 发送 HTTP 请求4. 创建 BeautifulSoup 对象5. 解析 HTML 内容5.1 查找标签5.2 根据属性…

Spring框架自带的定时任务:Spring Task详解

文章目录 一、基本使用1、配置&#xff1a;EnableScheduling2、触发器&#xff1a;Scheduled 二、拓展1、修改默认的线程池2、springboot配置 三、源码分析参考资料 一、基本使用 1、配置&#xff1a;EnableScheduling import org.springframework.context.annotation.Config…