网络粘包,拆包处理

2023-11-06

数据由一台设备通过网络发送给另一台设备普遍存在着网络上的 拆包与粘包问题,如图,有四个数据包
相同颜色表示一条完整的数据,对于 红色数据,它被拆分成了数据包一和数据包二,并与黑色和粉色数据同在一个数据包二中,这两个数据包就发生了粘包和拆包的现象。

如果我们单独地读取数据包一和数据包二,都无法读取到正确的数据,这时我们就需要对网络上的拆包和粘包现象进行处理


PS:基于TCP的网络传输框架netty实现可以看我的另一篇博文https://blog.csdn.net/qq_39658819/article/details/79389528


谢谢!


注意:以下代码只适用于类似与括号匹配型的数据包处理,比如json格式,或者{内容},{(内容)(内容)},这种数据格式

贴代码:

PackageUtil工具类

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * 网络上的数据包粘包和拆包问题解决类
 * 但不能解决丢包问题,且如果出现丢包问题,该类可能会导致长时间接收不到数据的问题
 * TCP不会出现丢包问题,但是UDP会
 * @author NewBies
 *
 */
public class PackageUtil {
	
	/**
	 * 用于存储括号,判断是否完整的读取到了一条数据
	 */
	public static Stack<String> bracket = new Stack<String>();
	/**
	 * 对于packageList中,在哪几个角标开始是完整的数据
	 */
	public static List<Integer> packageIndex = new ArrayList<Integer>();
	/**
	 * 用于暂存接收
	 */
	private static List<String> packageList = new ArrayList<String>();
	/**
	 * 上一个未读完的数据包
	 */
	public static String prePackage = "";
	/**
	 * 如果5个数据包都没有解析出一条完整的数据,则表示出错
	 */
	private static int timeToLive = 5; 
	/**
	 * 已生存时间
	 */
	private static int liveTime = 0;
	
	/**
	 * 传入一个数据包,对数据包进行判断,返回完整的报文
	 * @param packageString
	 * @return
	 * @throws Exception 
	 */
	public static List<String> match(String packageString) throws Exception{
		
		packageIndex.clear();
		packageList.clear();
		//进行括号匹配,确定是否存在完整的数据包
		for(int i = 0; i < packageString.length(); i++){
			if(packageString.charAt(i) == '{'||packageString.charAt(i) == '['||packageString.charAt(i) == '('){
				bracket.push(packageString.charAt(i) + "");
			}
			else if(packageString.charAt(i) == '}'||packageString.charAt(i) == ']'||packageString.charAt(i) == ')'){
				bracket.pop();
				//如果bracket为空,说明括号匹配成功
				if(bracket.isEmpty()){
					packageIndex.add(i);
				}
			}
		}
		
		prePackage += packageString;
		int beginIndex = 0;
		int endIndex = 0;
		
		//如果一个数据包中包含多个报文,那么将每一个报文添加到线性表中去
		for(int i = 0; i < packageIndex.size(); i++){
			//由于当前数据包与前一个未解析的数据包相连起来了,而解析出来的index是没有相连时的index,所以当前的结束长度应该加上未连接前
			//的未解析数据包的长度,即为prePackage.length() - packageString.length() + 1
			endIndex = packageIndex.get(i) + prePackage.length() - packageString.length() + 1;
			//将解析出来的数据添加到list中去
			packageList.add(prePackage.substring(beginIndex,endIndex));
			beginIndex = endIndex;
		}
		
		//当完整的报文截取完毕后就判断该数据包是否有剩下的不完整的报文(说明至少构成了一个完整的报文)
		if(packageIndex.size() > 0 && packageIndex.get(packageIndex.size() - 1) + 1 <= packageString.length()){
			//存储上不完整的数据包
			prePackage = packageString.substring(packageIndex.get(packageIndex.size() - 1) + 1,packageString.length());
		}
		
		//如果没有解析出一条完整的数据,那么就将liveTime加一
		if(packageList.size() == 0){
			liveTime++;
		}
		else{
			//只要解析出了至少一条完整的数据,那么就将liveTime置为0
			liveTime = 0;
		}
		//如果连续几次都没解析出一条完整的数据包,那么就认为网络上丢包了
		if(liveTime == timeToLive){
			throw new Exception("网络可能出现丢包问题");
		}
		return packageList;
	}

	/**
	 * 设置最大生存时间
	 * @param ttl
	 */
	public static void setTimeToLive(int ttl){
		PackageUtil.timeToLive = ttl;
	}
}

测试类:

PackageUtilTest:

public class PackageUtilTest {
	public static void main(String[] args) throws Exception{
		String temp1 = "{"
				+"\"type\" : \"4\","
				+"\"files\" : ["
				+ "{"	
				+"\"fileName\" : \"1.jpeg\","
				+"\"filePath\" : \"http://192.168.155.1:8080/download/1.jpeg\","
				+"\"fileType\":0,"
				+"\"adTime\" : 3000,"
				+"\"fileSize\":1024,"
				+"\"startTime\" : \"2018-2-15-18-01\","
				+"\"endTime\" : \"2018-5-14-18-01\""
				+"},"
				+"{"
				+"\"fileName\" : \"3.jpg\", "
				+"\"filePath\" : \"http://192.168.155.1:8080/download/3.jpg\","
				+"\"fileType\":0,"
				+"\"adTime\" : 5000,"
				+"\"fileSize\":1024,"
				+"\"startTime\" :";
		String temp2 =  
				"\"2018-2-5-18-00\","
				+"\"endTime\" : \"2019-1-14-12-00\""
				+"}"
				+"]" 
				+"}{";
		String temp3 = "asdaf{fa}"; 
		String temp4 = "}{1}{2}{3}{dfaf";	
		System.out.println("解析出来的完整数据有: " + PackageUtil.match(temp1));
		System.out.println("prePackage: " + PackageUtil.prePackage);
		System.out.println("解析出来的完整数据有: " + PackageUtil.match(temp2));
		System.out.println("prePackage: " + PackageUtil.prePackage);
		System.out.println("解析出来的完整数据有: " + PackageUtil.match(temp3));
		System.out.println("prePackage: " + PackageUtil.prePackage);
		System.out.println("解析出来的完整数据有: " + PackageUtil.match(temp4));
		System.out.println("prePackage: " + PackageUtil.prePackage);
	}
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络粘包,拆包处理 的相关文章

  • vscode ( Visual Studio Code )设置中文、字体和字号

    全拼是 Visual Studio Code 简称 VS Code 是由微软研发的一款免费 开源的跨平台代码编辑器 目前是前端 网页 开发使用最多的一款软件开发工具 下载网址 https code visualstudio com Down
  • WeChat结合文档开发

    1 获取accessToken 2 根据测试号给的ID和秘钥 生成accessToken 3 拿到accessToken之后 尝试使用其中的某一个接口 比如说消息推送 创建模板 生成模板ID 生成之后会产生模板ID 4 根据openID 模
  • 网络通信深入解析:探索TCP/IP模型

    http协议访问web 你知道在我们的网页浏览器的地址当中输入url 未必是如何呈现的吗 web浏览器根据地址栏中指定的url 从web服务器获取文件资源 resource 等信息 从而显示出web页面 web使用HTTP 超文本传输协议
  • linux bitcoin-qt程序运行时 缺少 libboost.so 动态库

    这是因为该程序在系统变量的路径下未找到自己的依赖库 所以就启动不了 执行将缺省的依赖库补上 能让程序搜索的到就可以了 bitcoin可执行程序的运行错误截图 执行打开后发生的错误提示 bitcoin qt home cly project
  • Linux epoll 详解

    最近 异想天开 想用D实现一个web服务器 似乎已经想这件事好久了 只不过之前是C 自然而然得开始研究epoll 早就听说过epoll的大名 只不过网上的教程似乎没多少 并且感觉也没怎么把用法给讲完整 好在 通过几天的学习 也算是有所积累
  • 解决使用InfluxDBClient报错influxdb.exceptions.InfluxDBClientError: 401 unauthorized

    解决方案 查看自己的InfluxDB数据库版本 如果版本是1 8 或是2 x 则 首先卸载influxdb pip uninstall influxdb 然后安装 pip install influxdb client ciso 后续使用i

随机推荐

  • K8S-微服务组件

    微服务组件包括哪些 一个完整的微服务包括的组件 注册中心 配置中心 熔断 限流 链路跟踪 路由 在微服务中 有些组件为必须组件 必须启动存在 客户端才能正常调用 必须组件 注册中心 后台服务 Provider 非必须组件 配置中心 熔断 限
  • select poll epoll

    Select select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理 用Select就可以完成非阻塞 所谓非阻塞方式non block 就是进程或线程执行此函数时不必非要等待事件的发生 select在socket编程中
  • xcodebuild使用

    转自 xcodebuild命令简单使用 简书 流程 build archive IPA teamid xcodebuild命令简单使用 前提准备证书并熟悉xcodebuild介绍 以及官方文档 xcodebuild showsdks查看可用
  • vue2安装ant-design UI报错 ERR! peer vue@“>=3.2.0“ from ant-design-vue@3.2.15

    npm install ant design vue save 安装报错 使用的是vue2 如图 npm ERR code ERESOLVE npm ERR ERESOLVE unable to resolve dependency tre
  • JSP request对象功能详解说明

    转自 JSP request对象功能详解说明 下文笔者讲述request对象的功能简介说明 如下所示 request的功能 request是HttpServletRequest类的实例 request对象中封装了客户端请求的数据信息 通过获
  • python --- 之pil图像转换的一些方式

    在数字图像处理中 针对不同的图像格式有其特定的处理算法 所以 在做图像处理之前 我们需要考虑清楚自己要基于哪种格式的图像进行算法设计及其实现 本文基于这个需求 使用python中的图像处理库PIL来实现不同图像格式的转换 对于彩色图像 不管
  • UNITY普通3D项目转换成URP项目

    注意 要确认 UniversalRP 对应的版本支持 目前此插件在2019和2020 2版本上已经得到认证 转换的前提 1 查看素材或询问素材支持的Unity版本 2 查看或询问素材是否支持 UniversalRP 插件 3 查看或询问素材
  • 睿智的目标检测48——Tensorflow2 搭建自己的Centernet目标检测平台

    睿智的目标检测48 Tensorflow2 搭建自己的Centernet目标检测平台 学习前言 什么是Centernet目标检测算法 源码下载 Centernet实现思路 一 预测部分 1 主干网络介绍 2 利用初步特征获得高分辨率特征图
  • IDEA启动Tomcat时报java.lang.IllegalStateException: Error starting child

    今天刚刚开始学习使用idea时 在网上查了资料后 写了一个简单的Servlet做测试 结果出现 2019 01 13 01 30 39 745 Artifact testidea war exploded Artifact is being
  • web服务器型号价格,web服务器的价格

    web服务器的价格 内容精选 换一换 华为云将可用的计算资源 按照一定折扣进行售卖 其价格随市场供需关系实时变化 这种打折销售 价格实时变化的计费模式称为 竞价计费 在该计费模式下 您可以以折扣价购买并使用弹性云服务器 性能与常规云服务器无
  • 什么是高防CDN?

    高防CDN是为了更好的服务网络而出现的 是通过高防DNS来实现的 高防CDN是通过智能化的系统判断来路 再反馈给用户 可以减轻用户使用过程的复杂程度 通过智能DNS解析 能让网站访问者连接到响应的服务器上 以避免某个服务器因访问者过多而瘫痪
  • 多任务学习综述:推荐系统多任务学习(multitask)的实战总结和常见问题(一)

    多任务学习算法系列的主要内容是回顾和总结自己2019 2021间在深度学习多任务学习算法 multitask 的工业实践和遇到的常见问题 欢迎更多同学讨论和指正 同时也宣传一波我们组在推荐方向的工作成果 大规模推荐算法库PaddleRec
  • C语言操作字符练习题---跟着鹏哥学C语言第二天打卡

    选A 这里的字符串 hello bit hello bit 0 此处是有空格 在字符串里每个符号包空格都是由阿斯科码值组成 所以每个字符都会各自存储一个字节的空间 包括后面隐藏的 0 字符串内都会有 strlen计算包括空格 所以sizeo
  • Webgl开发输入框兼容问题及开发注意的问题

    Webgl开发输入框 InputField组件 输入中文 说明 解决方案一通过重写原生的InputField组件来兼容 解决方案二通过打包后自己定义JS 总结 说明 最近一直再搞WEBGL平台的项目 花了好几天解决InputField组件输
  • WPF PasswordBox 绑定

    对于PasswordBox 可能很多人都会按着TextBox的路子 在ViewModel里面写一个属性 然后绑定到Password属性上 当你写完这一切的时候 你会突然收到Visual Studio的提示 Password并不是依赖属性 不
  • java面对对象(中.2)(多态性,终极父类,instanceof,包装类等)

    今天来讲java中的多态性 多态性我个人认为它是最重要 同时也是最难理解的 不过不用担心 下面就带你了解java中的多态性 多态性 是面向对象中最重要的概念 在Java中的体现 对象的多态性 父类的引用指向子类的对象 可以直接应用在抽象类和
  • 爬虫学习笔记

    基于CefSharp开发浏览器 一 项目搭建 CefSharp中文帮助文档 public partial class Form1 Form 如何解释 public 表示类的访问级别 公开 partial 表示部分类 就是说这些代码只是类的一
  • Prometheus连接多个指标

    Prometheus连接多个指标与聚合 初识PromSQL 一 Prometheus二进制操作符 一对一匹配One to one 一对多和多对一匹配 初识PromSQL PromSQL看起来简短整洁 通俗易懂 乍一看很简单 但是当真正使用它
  • Python读取xls文件报错:raise XLRDError(FILE_FORMAT_DESCRIPTIONS[file_format]+‘; not supported‘)

    Python读取xls文件报错 raise XLRDError FILE FORMAT DESCRIPTIONS file format not supported 只需要下载pyexcel xls pip install pyexcel
  • 网络粘包,拆包处理

    数据由一台设备通过网络发送给另一台设备普遍存在着网络上的 拆包与粘包问题 如图 有四个数据包 相同颜色表示一条完整的数据 对于 红色数据 它被拆分成了数据包一和数据包二 并与黑色和粉色数据同在一个数据包二中 这两个数据包就发生了粘包和拆包的