当前位置: 首页 > news >正文

netty报文解析之粘包半包问题

粘包问题

Netty 的粘包问题是指在网络传输过程中,由于 TCP 协议本身的特点,导致发送方发送的若干个小数据包被接收方合并成了一个大数据包。这种情况称为粘包。

TCP 协议是面向流的协议,没有数据边界,发送方发送的数据可能会被分成多个数据包进行发送,接收方则需要将这些数据包重新组装为原始数据。当接收方处理不当时,就可能会发生粘包等问题。

造成粘包问题的原因主要有以下几点:

  1. 传输的数据量过大或者传输速度过快。
  2. 数据包长度不固定,或者协议自定义导致变长。
  3. 接收方的读取缓存区大小设置不当。

解决粘包问题的方法有很多种,其中比较常用的方式包括以下几点:

  1. 定长解码器:针对长度固定的数据包,采用定长的编码和解码方式,可以有效避免粘包问题。
  2. 分隔符解码器:使用特定字符或字符串作为数据包的分隔符,在接收方收到分隔符时进行消息的解码。
  3. 消息头加长度字段:在数据包中添加一部分用来表示数据包长度的信息,以便于接收方进行消息的解码和切割。
  4. 自定义协议:设计自己的消息传输协议,包括消息格式、头部、长度字段等,来解决粘包问题。
半包问题

半包问题是指在网络传输过程中,接收方无法完整地接收到一个数据包,而只接收到了部分数据包的情况。这种情况称为半包。

造成半包问题的主要原因是数据包的长度超过了接收方的缓存区大小,导致接收方无法一次性接收完整的数据包。协议设计不合理、网络延迟等也可能引起半包问题。

解决半包问题方法和解决粘包问题基本一致。

下面看下具体的例子

定长报文

定长报文就是收发双方约定一次通信的报文长度是固定长度的,服务端按照规定长度接收,客户端按照固定长度返送。这里主要用到FixedLengthFrameDecoder解码器,其构造函数有一个入参来指定报文的长度。

server:

pipeline.addLast(new FixedLengthFrameDecoder(1024))

发送数据

// 消息解析
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String receivedMessage = new String(bytes, "UTF-8");
System.out.println("接收到消息:" + receivedMessage);
// 发送响应
String responseMessage = "Response";
byte[] responseBytes = responseMessage.getBytes("UTF-8");
ByteBuf responseBuf = ctx.alloc().buffer(responseBytes.length);
responseBuf.writeBytes(responseBytes);
ctx.writeAndFlush(responseBuf);// 释放资源
buf.release();

client:

客户端只要每次发送按约定长度组装报文即可

固定长度头

固定长度头就是报文整体有两部分组成:报文头+报文体。齐总报文头是固定位置长度,里面会表明报文体长度,消息接收方先定长读取报文头,然后根据报文头指定的报文体长度来定量读取报文体。

这里用到了LengthFieldBasedFrameDecoder解码器。

该解析其有几个重要参数:

maxFrameLength:最大消息长度,报文最大长度

lengthFieldOffset:长度字段的偏移量,如有些报文可能报文头上还有一些其它的标识位,可以将这些标识位跳过

lengthFieldLength:长度字段的长度

lengthAdjustment:长度调整值,这个值也有一定的用处。有些情况长度标识的是包含header头的长度,这个时候可以将该值配置成负数,最后继续往后解析的长度是:lengthFieldLength+lengthAdjustment

initialBytesToStrip:从开始位置截取掉的字节长度,可以把header去掉再往后传给下一个handler,不过一般会保留报文头,业务代理再去解析。LengthFieldBasedFrameDecoder只负责报文接收完整。

整个处理流程:

当接收到来自网络的字节流时,LengthFieldBasedFrameDecoder 首先根据指定的 lengthFieldOffset 和 lengthFieldLength 定位长度字段的位置,并读取长度字段的值。

接下来,根据读取到的长度字段值计算出消息的长度。如果消息的长度超过了指定的 maxFrameLength,则会触发异常处理机制。

如果消息的长度合法,则 LengthFieldBasedFrameDecoder 会读取接下来的指定长度的字节,构成一个完整的消息。

最后,根据配置的 initialBytesToStrip 参数,可以选择是否去除消息长度头。

解码器完成后,将解析出的完整消息传递给下一个处理器进行进一步的处理。

用例:

如我们定义以下一种报文:

长度头(4字节,只是报文体长度)+标识位(1字节)+报文体长度。

则创建LengthFieldBasedFrameDecoder要指定。

lengthFieldOffset=0,lengthFieldLength=4,lengthAdjustment=1,initialBytesToStrip=0(保留报文头)

具体代码:

server端pipeline添加LengthFieldBasedFrameDecoder解码器和FixedLengthServerHandler

pipeline.addLast(new LengthFieldBasedFrameDecoder(1024,0,4,1,0));
pipeline.addLast(new FixedLengthServerHandler());

FixedLengthServerHandler处理方法如下:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;//消息解析int length = buf.readInt();byte[] bytes = new byte[length];char flag = (char) buf.readByte();buf.readBytes(bytes);String receivedMessage = new String(bytes, "UTF-8");System.out.println("接收到消息:" + receivedMessage+",消息标识:"+flag);// 发送响应String responseMessage = "SUCC";byte[] responseBytes = responseMessage.getBytes("UTF-8");int responseLength = responseBytes.length;ByteBuf responseBuf = ctx.alloc().buffer(4 +1+ responseLength);responseBuf.writeInt(responseLength);responseBuf.writeBytes("Y".getBytes());responseBuf.writeBytes(responseBytes);ctx.writeAndFlush(responseBuf);buf.release();
}

client端:

同样的pipeline添加两个handler

pipeline.addLast(new LengthFieldBasedFrameDecoder(1024,0,4,1,0));
pipeline.addLast(new FixedLengthClientHandler());

构造消息发送:

ByteBuf buffer = Unpooled.buffer();
byte[] bytes = "hello".getBytes();
buffer.writeInt(bytes.length);
buffer.writeBytes("X".getBytes());
buffer.writeBytes(bytes);
channel.writeAndFlush(buffer);

FixedLengthClientHandler处理响应报文:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;// 消息处理int length = buf.readInt();char flag = (char) buf.readByte();byte[] bytes = new byte[length];buf.readBytes(bytes);String receivedMessage = new String(bytes, "UTF-8");System.out.println("接收到消息:" + receivedMessage+",flag="+flag);buf.release();
}

另外这里处理的都是字节流数据,使用原先阻塞BIO socket也是可以的,不局限于ByteBuf。

如socket发送接收上面定长报文头数据:

Socket socket = new Socket("localhost", 8080);
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();// 发送消息
String message = "Hello";
byte[] data = message.getBytes();ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(data.length);outputStream.write(buffer.array());
outputStream.write("X".getBytes());
outputStream.write(data);
outputStream.flush();
//接收响应
byte[] lenB = new byte[4];
inputStream.read(lenB);
char flag = (char) inputStream.read();
ByteBuffer buff = ByteBuffer.wrap(lenB);
int len = buff.getInt();byte[] resp = new byte[len];
inputStream.read(resp);
System.out.println("响应:"+new String(resp) +",flag="+flag);outputStream.close();
inputStream.close();
socket.close();
分隔符报文

分隔符报文就是将报文按固定字符进行分割,这里使用DelimiterBasedFrameDecoder 解析器。

入参可指定分隔符及最大报文长度。

与之相似的还有LineBasedFrameDecoder按行读取,就是以 '\n’换行符当作分隔符。

自定义报文

基本上LengthFieldBasedFrameDecoder解码器已经满足解决报文粘包问题,如果还有其它比较复杂的报文,可以自定义协议报文格式进行处理,一个基本原则还是要有一个报文长度标识,然后按具体长度进行读取。

相关文章:

netty报文解析之粘包半包问题

粘包问题 Netty 的粘包问题是指在网络传输过程中,由于 TCP 协议本身的特点,导致发送方发送的若干个小数据包被接收方合并成了一个大数据包。这种情况称为粘包。 TCP 协议是面向流的协议,没有数据边界,发送方发送的数据可能会被分…...

EasyCode整合mybatis-plus的配置

文章目录 entitymapper.javamapper.xmlserviceserviceImplcontroller 这篇文章不教你如何安装和使用EasyCode,只是贴出可以使用的配置。 具体EasyCode的使用可以查看其它的文章。 entity ##导入宏定义 $!{define.vm}##保存文件(宏定义) #sa…...

实施预测性维护解决方案的挑战及PreMaint的应对方法

前面我们介绍了企业选择预测性维护解决方案的常见问题和PreMaint的策略,本期我们将带来实施过程中可能会遇到的挑战,以及如何通过PreMaint来应对这些挑战,以实现可靠的预测性维护。 随着工业技术的不断进步,预测性维护作为一种先进…...

1. js中let、var、const定义变量区别与方式

1 声明语法 var upperA A; let upperB B; const upperC C; 只声明不初始化的结果,【 const定义的常量不可以修改,而且必须初始化】 // var 声明变量 var upperA; console.log(打印大写的A:%s, upperA); // 结果:打印大写的A&am…...

【STM32学习】I2C通信协议 | OLED屏

🐱作者:一只大喵咪1201 🐱专栏:《STM32学习》 🔥格言:你只管努力,剩下的交给时间! 今天需要将代码烧录到开发板中,本喵默认大家都会创建工程,以及进行基本的…...

Nvme Spec 第一章节学习

Nvme Express Base Specification 第一章 简介 1.1概述 NVM ExpressTM(NVMeTM)接口允许主机软件与非易失性存储器子系统通信。 此接口针对企业和客户端固态驱动器进行了优化,通常作为寄存器级接口连接到PCI Express接口。 注:在…...

第一章:最新版零基础学习 PYTHON 教程(第九节 - Python 语句中的 – 多行语句)

Python 中的语句: 在Python中,语句是Python解释器可以读取和执行的逻辑命令。它可能是Python 中的赋值语句或表达式。 Python 中的多行语句: 在Python中,语句通常写成一行,每行的最后一个字符是换行符。要将语句扩展到一行或多行,我们可以使用大括号 {}、圆括号 ()、方…...

kafka 3.0 离线安装

1.安装zookeeper 解压apache-zookeeper-3.8.0-bin.tar.gz到指定目录,复制conf目录下zoo_sample.cfg到zoo.cfg,并修改配置。 # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit…...

MySQL数据库入门到精通2--基础篇(函数,约束,多表查询,事务)

3. 函数 函数 是指一段可以直接被另一段程序调用的程序或代码。MySQL中的函数主要分为以下四类: 字符串函数、数值函数、日期函数、流程函数。 3.1 字符串函数 MySQL中内置了很多字符串函数,常用的几个如下: 演示如下: A. con…...

c-数据在内存中的存储-day7

...

3D大模型如何轻量化?试试HOOPS Communicator,轻松读取10G超大模型!

随着计算机技术的不断发展,3D模型在各行各业中的应用越来越广泛。然而,随着模型的复杂性和规模不断增加,处理和浏览超大型3D模型变得越来越具有挑战性。本文将探讨如何轻量化3D大模型,以及如何使用HOOPS Communicator来读取和浏览…...

go并发操作且限制数量

使用管道chan func returnNum() int64 {return time.Now().Unix() } func main() {threadAmount : runtime.GOMAXPROCS(0)if threadAmount < 2 {threadAmount 2}fmt.Println(threadAmount)threadChan : make(chan int, threadAmount)defer close(threadChan)for {for i :…...

AI深度学习-卷积神经网络000

文章目录 前言1.什么是深度学习2.语义分割与实例分割概述3.什么是卷积&#xff1f;4.Unet网络 前言 本栏目&#xff0c;主要为深度学习保姆教程。 主要通过B站视频整理而来&#xff1a; 深度学习保姆级教学 Unet语义分割视觉三维重建算法 1.什么是深度学习 深度学习保姆级教…...

网站有反爬机制就爬不了数据?那是你不会【反】反爬

目录 前言 一、什么是代理IP 二、使用代理IP反反爬 1.获取代理IP 2.设置代理IP 3.验证代理IP 4.设置代理池 5.定时更新代理IP 三、反反爬案例 1.分析目标网站 2.爬取目标网站 四、总结 前言 爬虫技术的不断发展&#xff0c;使得许多网站都采取了反爬机制&#xff…...

2023华为杯研究生数学建模C题分析

完整的分析查看文末名片获取&#xff01; 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作品集合之间应有一些交集。但有的交集大了&#xff0c;则…...

第三天:实现网络编程基于tcp/udp协议在Ubuntu与gec6818开发板之间双向通信

互联网地址 每一台设备接入互联网后&#xff0c;都会举报一个唯一的地址编号 IP地址 INTERNET地址 internet地址 &#xff1a;它是协议上的一个逻辑地址 目前来说&#xff0c;我们主要的IP地址有两类 IPV4 IPV6 IPV4 其实就是使用一个32bit整数作为IP IPV6 其实就是使用一…...

【MediaSoup---源码篇】(三)Transport

概述 RTC::Transport是mediasoup中的一个重要概念&#xff0c;它用于在mediasoup与客户端之间传输实时音视频数据。 Transport继承着众多的类&#xff0c;主要用于Transport的整体感知 class Transport : public RTC::Producer::Listener,public RTC::Consumer::Listener,publ…...

爱分析《商业智能最佳实践案例》

近日&#xff0c;国内知名数字化市场研究咨询机构爱分析发布《2023爱分析商业智能最佳实践案例》&#xff0c;此评选活动面向落地商业智能的各行企业和商业智能厂商&#xff0c;以第三方专业视角深入调研&#xff0c;评选出具有参考价值的创新案例。永达汽车集团与数聚股份合作…...

golang:context

context作用 goroutine的退出机制 多个goroutine都是平行的被调度的&#xff0c;多个goroutine如何协调工作涉及通信、同步、通知和退出 通信&#xff1a;goroutine之间的通信同步chan通道 同步&#xff1a;不带缓冲的chan提供了一个天然的同步等待机制。通过WaitGroup也可以…...

探讨代理IP与Socks5代理在跨界电商中的网络安全应用

在数字化时代&#xff0c;跨界电商已经成为了商业世界中的一大趋势。然而&#xff0c;跨越国界的电商活动也伴随着网络安全挑战。本文将讨论如何利用代理IP和Socks5代理技术来提高跨界电商中的网络安全&#xff0c;同时也探讨了与游戏相关的爬虫应用。 1. 代理IP和Socks5代理的…...

Guava Cache介绍-面试用

一、Guava Cache简介 1、简介 Guava Cache是本地缓存&#xff0c;数据读写都在一个进程内&#xff0c;相对于分布式缓存redis&#xff0c;不需要网络传输的过程&#xff0c;访问速度很快&#xff0c;同时也受到 JVM 内存的制约&#xff0c;无法在数据量较多的场景下使用。 基…...

ARM 汇编指令作业(求公约数、for循环实现1-100之间和、从SVC模式切换到user模式简单写法)

1、求两个数最大公约数 .text .globl _start_start:mov r0, #9mov r1, #15 Loop: 循环cmp r0,r1 比较r0和r1的大小beq stop 当r0和r1相等时&#xff0c;跳到stop标签subhi r0,r0,r1 r0-r1>0 时&#xff0c;证明r0>r1,将r0-r1的值赋给r0&…...

Go - 【字符串,数组,哈希表】常用操作

一. 字符串 字符串长度&#xff1a; s : "hello" l : len(s) fmt.Println(l) // 输出 5遍历字符串&#xff1a; s : "hello" for i, c : range s {fmt.Printf("%d:%c ", i, c) } // 输出&#xff1a;0:h 1:e 2:l 3:l 4:ofor i : 0; i < le…...

vue 普通组件的 局部注册

vue 普通组件的 注册 11 Vue2_3入门到实战-配套资料\01-随堂代码素材\day03\素材\00-准备代码\小兔鲜首页静态页\src...

医疗虚拟仿真和虚拟现实有什么区别?哪个更好?

随着我们在仿真教育中越来越多地使用新技术&#xff0c;区分虚拟模式的类型很重要。虚拟仿真是一个统称&#xff0c;用来概括术语来描述各种基于仿真的体验&#xff0c;从基于屏幕的平台到沉浸式虚拟现实。然而&#xff0c;各虚拟平台在保真度、沉浸感和临场感的水平上有很大差…...

【.net core】yisha框架使用nginx代理swagger接口无法访问问题

后端代码配置 #在StartUp.cs文件中Configure方法中增加以下代码 app.UseSwagger(c >{//代理路径访问c.PreSerializeFilters.Add((doc, item) >{//根据代理服务器提供的协议、地址和路由&#xff0c;生成api文档服务地址doc.Servers new List<OpenApiServer>{ new…...

uniapp录音功能和音频播放功能制作

录音功能 在 UniApp 中&#xff0c;你可以使用 uni.getRecorderManager() 方法来创建一个录音管理器实例&#xff0c;从而实现录音功能。 以下是一个示例&#xff0c;演示了如何在 UniApp 中使用 uni.getRecorderManager() 实现录音功能&#xff1a; // 在需要录音的页面或组…...

服务器数据恢复-LINUX操作系统下各文件系统误删除/格式化数据的恢复方案

服务器数据恢复环境&#xff1a; 基于EXT2/EXT3/EXT4/Reiserfs/Xfs文件系统的Linux操作系统。 服务器故障&#xff1a; LINUX操作系统下误删除/格式化数据。 服务器数据恢复过程&#xff1a; 1、首先会检测服务器是否存在硬件故障&#xff0c;如果检测出硬件故障&#xff0c;交…...

python/C++二分查找库函数(lower_bound() 、upper_bound,bisect_left,bisect_right)

二分查找是一种经典的搜索算法&#xff0c;广泛应用于有序数据集中。它允许在大型数据集中高效地查找目标元素&#xff0c;减少了搜索的时间复杂度。本文将介绍在 C 和 Python 中内置的二分查找函数&#xff0c;让二分查找变得更加容易。 c lower_bound() 、upper_bound 定义…...

爬虫 — App 爬虫(二)

目录 一、Appium介绍二、node.js 安装三、Java 的 SDK 安装以及配置1、安装步骤2、配置环境变量 四、安卓环境的配置1、配置环境变量 五、Appium 安装1、安装2、打开 APP3、使用 六、Appium 使用1、定位数据&#xff08;方法一&#xff0c;不常用&#xff09;2、定位数据&#…...

网站正在建设中中文模板/平台优化是指什么

1、传统媒体不传统 当今媒体圈&#xff0c;最热门的词汇莫过于“新媒体”。新媒体究竟是什么&#xff0c;10个人大概会给10个不同的定义。我现在给一个比较靠谱的定义&#xff1a;新媒体是基于大数据的一种传播方式。 媒体是什么?媒体就是传播信息的媒介。这句话里有两个要害。…...

营销型网站建设都具有哪些优势/网络营销师是做什么的

Shell脚本就是将很多条命令结合起来写在一起&#xff0c;通过运算以及判断来实现很多功能的文本。为什么学习shell&#xff0c;首先一些常规的系统管理操作&#xff0c;并且需要自动化地执行&#xff0c;如果用shell脚本把这些操作集中在一起&#xff0c;只需要定期的执行这个s…...

兰州专业做网站/seo排名软件怎么做

undefined reference to __gxx_personality_v0 刚刚在做一个实验&#xff0c;编译的时候出了一点问题&#xff0c;下面附上代码&#xff1a;#include <stdio.h> #include <stdlib.h> #include <unistd.h>static void my_exit1(void); static void my_exit2(v…...

推广营销软件/百度seo优化技术

软件危机 软件危机泛指在计算机软件的开发和维护过程中遇到的一系列严重的问题&#xff0c;集中表现在成本&#xff0c;质量&#xff0c;生产效率等几个方面。 软件危机 软件工程 软件工程是指采用工程的概念、原理、技术和方法指导软件的开发与维护。软件工程的主要思想强调…...

有什么做设计接任务的网站/排名优化推广

在本章中&#xff0c;我们将介绍以下食谱&#xff1a; 使用授权码授予类型保护资源支持隐式授予类型使用“资源所有者密码凭证”授予类型作为OAuth 2.0迁移的方法配置客户端凭据授予类型添加对刷新令牌的支持使用关系数据库存储令牌和客户详细信息使用Redis作为令牌存储实施客户…...

新吴区住房和城乡建设部网站/东莞网站建设优化

如何遏制PostgreSQL WAL的疯狂增长 作者&#xff1a;陈华军 / 2017-6-12 欢迎大家踊跃投稿&#xff0c;投稿信箱&#xff1a;presspostgres.cn 前言 PostgreSQL在写入频繁的场景中&#xff0c;会产生大量的WAL日志&#xff0c;而且WAL日志量会远远超过实际更新的数据量。 我…...