Netty粘包与拆包问题
先看一下下面的例子:
服务端代码为:
public class TimeServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup=new NioEventLoopGroup();EventLoopGroup workerGroup=new NioEventLoopGroup();try{ServerBootstrap b=new ServerBootstrap();b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel channel) {channel.pipeline().addLast(new TimeServerHandler());}});ChannelFuture f=b.bind(8081).sync();f.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
class TimeServerHandler extends SimpleChannelInboundHandler {private int counter;@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf=(ByteBuf)msg;byte[] req=new byte[buf.readableBytes()];buf.readBytes(req);String body=new String(req, StandardCharsets.UTF_8);System.out.println(body+",counter:"+ (++counter));}
}
客户端代码为:
public class TimeClient {public static void main(String[] args) throws InterruptedException {EventLoopGroup group=new NioEventLoopGroup();try{Bootstrap b=new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel){socketChannel.pipeline().addLast(new TimeClientHandler());}});ChannelFuture f=b.connect("127.0.0.1",8081).sync();f.channel().closeFuture().sync();}finally {group.shutdownGracefully();}}
}
public class TimeClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) {byte[] req= ("send msg to server\r\n").getBytes(StandardCharsets.UTF_8);for (int i = 0; i < 3; i++) {ByteBuf msg= Unpooled.buffer(req.length);msg.writeBytes(req);ctx.writeAndFlush(msg);}}
}
上面的代码中,客户端会连续发三条信息给服务端,服务端接收信息并进行计数。
或许你会认为服务端会收到三条信息,但事实上服务端可能只会收到一条,结果为:
send msg to server\send msg to server\send msg to server,counter:1
这是因为发生了粘包。粘包指的是多个小的数据包可能被封装成一个大的数据包发送。与之相关的是拆包,拆包指的是一个完整的数据包可能会被 TCP 拆分成多个包进行发送。
出现粘包/拆包的原因
出现粘包/拆包的原因有三点:
- 应用发送的字节数超过 Socket 发送缓冲区的大小。每个 TCP Socket 连接在内核中都有一个发送缓冲区和接收缓冲区,在接收数据时,会把数据存到接收缓冲区,等待用户读取。
- 对超过 TCP 最大报文段长度(Max Segment Size,MSS)进行 TCP 分段。
- 对以太网帧超过最大传输单元(Maximum Transmission Unit,MTU)进行 IP 分片。因为数据链路层传输的帧大小是有限制的,MTU 指的就是以太网和 IEEE 802.3 对数据帧的长度限制。
如果 IP 需要发送一个数据报,并且这个数据报比链路层 MTU 大,则 IP 会通过分片将数据报分解成较小的部分,使每个分片都小于 MTU。 当两台主机之间跨越多个网络通信时,每条链路可能有不同大小的 MTU。在包含所有链路的整个网络路径上,最小的 MTU 称为路径 MTU。
粘包问题的解决办法
因为底层的 TCP 无法区分上层的应用数据,所以只能依赖上层来解决,主要有四种办法:
- 固定消息的长度
- 在包尾增加回车或换行符以进行分割
- 将消息分为消息头和消息体,消息头中包含表示消息总长度 (或者消息体长度) 的字段。
- 使用更复杂的应用层协议。
Netty 提供了 LineBasedFrameDecoder
和 StringDecoder
解决粘包问题。
public class TimeServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup=new NioEventLoopGroup();EventLoopGroup workerGroup=new NioEventLoopGroup();try{ServerBootstrap b=new ServerBootstrap();b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel channel) { //添加LineBasedFrameDecoder和StringDecoderchannel.pipeline().addLast(new LineBasedFrameDecoder(1024));channel.pipeline().addLast(new StringDecoder());channel.pipeline().addLast(new TimeServerHandler());}});ChannelFuture f=b.bind(8081).sync();f.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
class TimeServerHandler extends SimpleChannelInboundHandler {private int counter;@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) {String body=(String) msg; //接收到的是String类型System.out.println(body+",counter:"+ (++counter));}
}
LineBasedFrameDecoder
的工作原理是依次遍历 ByteBuf 中的可读字节,判断是否有“\n”或者“\r\n”,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标志的解码器。
StringDecoder
则会将接收到的对象转换成字符串。
参考:
- 《Netty 权威指南》
- 《TCP/IP 详解卷 1:协议》
- TCP的发送缓冲区和接收缓冲区 - 取经路上的白龙马C - 博客园
- MTU TCP-MSS详解 - 知乎
相关文章:
Netty粘包与拆包问题
先看一下下面的例子: 服务端代码为: public class TimeServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroupnew NioEventLoopGroup();EventLoopGroup workerGroupnew NioEventLoopGroup();try{Serv…...
JS下载链接的两种方式
1、window.open() 弹出新窗口下载,需要用户进行交互之后触发,否则会被拦截,Safari始终会拦截弹窗 2、a标签下载 不会触发弹窗,更安全 let a document.createElement(a) a.href 下载链接; a.download 文件名称; document.bod…...

手把手教你实现:将后端SpringBoot项目部署到华为云服务器上
前言 前提:有一个后端项目,项目能够运行在本地,可以通过本地访问(localhost) 如果没有可以看这篇:一个基于SpringBoot的后端项目 注册华为云账号 华为云官网 购买云服务器 产品 -> 华为云耀云服务器…...

【红队攻防】从零开始的木马免杀到上线
0、环境配置说明 应该全部使用云服务器完整演示比较好,奈何太穷了买不起服务器,只能用本地环境演示。所需环境如下: 系统环境: CentOS 7 ,Windows 10 软件环境 Cobalt Strike 4.7 , ShellQMaker, 360杀…...
Linux命令行操作:使用“more“命令进行分页显示
文章目录 1. 引言1.1 介绍Linux操作系统和命令行界面什么是Linux操作系统?为什么命令行界面在Linux中如此重要? 1.2 介绍Linux中的分页显示命令分页显示命令的作用与意义不同分页显示命令的比较 2. "more"命令的基本用法2.1 安装和启动"m…...

CentOS下安装MySQL 8.1及备份配置
1 卸载原来的MySQL版本 移除之前部署的mysql软链接 # unlink /etc/init.d/mysql # unlink /usr/bin/mysql2 下载最新的MySQL版本 https://dev.mysql.com/downloads/mysql/8.0.html 我这里直接把地址放在这里:https://cdn.mysql.com//Downloads/MySQL-8.1/mysql…...

【RabbitMQ实战】06 3分钟部署一个RabbitMQ集群
一、集群的安装部署 我们还是利用docker来安装RabbitMQ集群。3分钟安装一个集群,开始。 前提条件,docker安装了docker-compose。如果没安装的话,参考这里 docker-compose文件参考bitnami官网:https://github.com/bitnami/contai…...
(c语言)整形提升
#include<stdio.h> //整形提升 int main() { char a 5; //字符型的内存大小为8个比特位,故在进行加法之类的线性运算时需要整形提升 //00000000000000000000000000000101->5 因为字符型的内存大小不足,故在存放整形时需要裁切 …...
上传文件报错:The temporary upload location [/tmp/tomcat/xxx] is not valid
1.上传附加时报错找不到临时目录 Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat/work/Tomcat/localhost/ROOT] is not valid 发生改报错原因为 (1)、SpringBoot项目启动后,系…...

直线模组的品牌有哪些?
中国工业制造业快速发展,工业自动化领域也进入了飞速发展的阶段,直线模组作为工业自动化领域不可缺少的机器人之一,有着重要的不可或缺的作用,在行业内做得好的直线模组品牌有哪些呢? 1、NSK:日本精工株式会…...

零基础学习ESP8266
文章目录 零基础学习ESP8266前言选择硬件如何学习专栏大纲基础部分提高部分 总结 零基础学习ESP8266 前言 最近在空余的时候有用乐鑫的模组,感觉很不错,也决定简单写写。 相信看这篇文章的同学,希望可以熟悉ESP8266这个硬件平台。当然我们…...

基于PYQT5的GUI开发系列教程【二】框架安装和基础环境配置
本文概述 PYQT5是一个基于python的可视化GUI开发框架,具有容易上手,界面美观,多平台部署等优点,作者将通过一系列教程,带领大家从零基础到入门~能够自主实现GUI开发。 作者介绍 作者本人是一名人工智能炼丹师ÿ…...
pg数据库操作,insert(sql)插入一条数据后获返回当前插入数据的id --chatGPT
gpt: 在 PostgreSQL 数据库中,可以使用 INSERT 语句插入一条数据,并通过 RETURNING 子句来返回插入数据的 ID。以下是一个示例 Go 代码来执行这个操作: go package main import ( "database/sql" "fmt" &…...

【数据结构-树】哈夫曼树
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

HarmonyOS 4.0 实况窗上线!支付宝实现医疗场景智能提醒
本文转载自支付宝体验科技,作者是蚂蚁集团客户端工程师博欢,介绍了支付宝如何基于 HarmonyOS 4.0 实况窗实现医疗场景履约智能提醒。 1.话题背景 8 月 4 日,华为在 HDC(华为 2023 开发者大会)上推出了新版本操作系统…...

【响应式布局】
响应式布局 1 什么是响应式布局2 响应式布局的5种实现方案2.1 百分比布局2.2 媒体查询布局2.3 rem响应式布局2.4 vw / vh响应式布局2.5 flex弹性布局 1 什么是响应式布局 响应式布局就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。这个概念是为解决移动互…...

Spring面试题23:Spring支持哪些事务管理类型?Spring框架的事务管理有哪些优点?你更倾向用哪种事务管理类型?
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring支持哪些事务管理类型? Spring 支持以下几种事务管理类型: 编程式事务管理:通过在代码中显式地使用事务管理 API(如 TransactionTempla…...
Leetcode—— LCR 122. 路径加密
LCR 122. 路径加密 假定一段路径记作字符串 path,其中以 "." 作为分隔符。现需将路径加密,加密方法为将 path 中的分隔符替换为空格 " ",请返回加密后的字符串。 示例 1: 输入:path "a.ae…...

缓冲区溢出漏洞分析
一、实验目的 熟悉软件安全需求分析方法,掌握软件安全分析技术。 二、实验软硬件要求 1、操作系统:windows 7/8/10等 2、开发环境:VS 6.0(C)、OllyDbg 三、实验预习 《软件安全技术》教材第3章 四、实验内容&#…...

【高阶数据结构】红黑树(C++实现)
⭐博客主页:️CS semi主页 ⭐欢迎关注:点赞收藏留言 ⭐系列专栏:C进阶 ⭐代码仓库:C进阶 家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...