Android 粘包与丢包处理工具类:支持多种粘包策略的 Helper 实现

在Android开发中,处理TCP/UDP通信时,粘包和丢包是常见的问题。粘包是指多个数据包被接收方一次性接收,导致数据包之间的界限不清晰;丢包则是指数据包在传输过程中丢失。为了处理这些问题,我们可以编写一个帮助类 PacketHelper,并提供不同的粘包处理策略。
功能概述

不处理粘包 :直接返回原始数据。

特定字符开始和结束 :通过指定的开始字符和结束字符标识数据包边界。

固定长度 : 每个数据包的长度固定。

可变长度 : 通过包头中的长度字段动态解析数据包长度。

从指定位置截取 :支持从数据包的指定位置开始截取到指定位置结束。

适用场景
TCP/UDP 通信中的数据粘包与丢包处理。

自定义协议的解析与数据包拆分。

需要灵活支持多种粘包策略的场景。

核心类与接口
PacketHandler 接口:定义统一的粘包处理接口。

NoOpPacketHandler:不处理粘包。

DelimitedPacketHandler:支持不同的开始字符和结束字符。

RangePacketHandler:支持从指定位置截取数据。

FixedLengthPacketHandler:处理固定长度的数据包。

VariableLengthPacketHandler:处理可变长度的数据包。

PacketHelper:封装粘包处理逻辑,提供统一调用接口。

1. 定义接口
首先,定义一个统一的接口 PacketHandler,用于处理不同的粘包策略:

public interface PacketHandler {byte[] handlePacket(byte[] data);
}

2. 实现不同的粘包处理策略
2.1 不处理粘包

直接返回原始数据,不做任何处理:

public class NoOpPacketHandler implements PacketHandler {@Overridepublic byte[] handlePacket(byte[] data) {return data;}
}

2.2 支持不同的开始字符和结束字符
通过指定的开始字符和结束字符来标识数据包的边界:

public class DelimitedPacketHandler implements PacketHandler {private final byte[] startDelimiter; // 开始字符private final byte[] endDelimiter;   // 结束字符public DelimitedPacketHandler(byte[] startDelimiter, byte[] endDelimiter) {this.startDelimiter = startDelimiter;this.endDelimiter = endDelimiter;}@Overridepublic byte[] handlePacket(byte[] data) {int startIndex = indexOf(data, startDelimiter);if (startIndex == -1) {return null; // 没有找到开始字符}int endIndex = indexOf(data, endDelimiter, startIndex + startDelimiter.length);if (endIndex == -1) {return null; // 没有找到结束字符}// 返回从开始字符到结束字符之间的数据return Arrays.copyOfRange(data, startIndex, endIndex + endDelimiter.length);}// 查找字节数组中指定模式的起始位置private int indexOf(byte[] data, byte[] pattern) {return indexOf(data, pattern, 0);}// 从指定位置开始查找字节数组中指定模式的起始位置private int indexOf(byte[] data, byte[] pattern, int fromIndex) {for (int i = fromIndex; i <= data.length - pattern.length; i++) {boolean match = true;for (int j = 0; j < pattern.length; j++) {if (data[i + j] != pattern[j]) {match = false;break;}}if (match) {return i;}}return -1;}
}

2.3 支持从指定位置到指定位置的截取
从数据包的指定位置开始截取,到指定位置结束

public class RangePacketHandler implements PacketHandler {private final int startIndex; // 开始位置private final int endIndex;   // 结束位置public RangePacketHandler(int startIndex, int endIndex) {this.startIndex = startIndex;this.endIndex = endIndex;}@Overridepublic byte[] handlePacket(byte[] data) {if (data.length < endIndex) {return null; // 数据长度不足}return Arrays.copyOfRange(data, startIndex, endIndex);}
}

2.4 固定长度
每个数据包的长度是固定的:

public class FixedLengthPacketHandler implements PacketHandler {private final int packetLength;public FixedLengthPacketHandler(int packetLength) {this.packetLength = packetLength;}@Overridepublic byte[] handlePacket(byte[] data) {if (data.length >= packetLength) {return Arrays.copyOfRange(data, 0, packetLength);}return null;}
}

2.5 可变长度
数据包的长度是可变的,通过包头中的长度字段来确定:

public class VariableLengthPacketHandler implements PacketHandler {private final int lengthFieldOffset;private final int lengthFieldLength;public VariableLengthPacketHandler(int lengthFieldOffset, int lengthFieldLength) {this.lengthFieldOffset = lengthFieldOffset;this.lengthFieldLength = lengthFieldLength;}@Overridepublic byte[] handlePacket(byte[] data) {if (data.length >= lengthFieldOffset + lengthFieldLength) {int packetLength = parseLength(data, lengthFieldOffset, lengthFieldLength);if (data.length >= packetLength) {return Arrays.copyOfRange(data, 0, packetLength);}}return null;}private int parseLength(byte[] data, int offset, int length) {int result = 0;for (int i = 0; i < length; i++) {result = (result << 8) | (data[offset + i] & 0xFF);}return result;}
}
  1. 使用帮助类
    PacketHelper 类用于封装粘包处理逻辑,提供统一的接口:
public class PacketHelper {private PacketHandler packetHandler;public PacketHelper(PacketHandler packetHandler) {this.packetHandler = packetHandler;}public byte[] processPacket(byte[] data) {return packetHandler.handlePacket(data);}
}
  1. 示例用法
    以下是使用不同粘包处理策略的示例:
public class Main {public static void main(String[] args) {// 示例数据byte[] data = "STARTHelloWorldEND".getBytes();// 1. 使用不同的开始字符和结束字符PacketHelper helper1 = new PacketHelper(new DelimitedPacketHandler("START".getBytes(), "END".getBytes()));byte[] packet1 = helper1.processPacket(data);if (packet1 != null) {System.out.println(new String(packet1)); // 输出: STARTHelloWorldEND}// 2. 使用从指定位置到指定位置的截取PacketHelper helper2 = new PacketHelper(new RangePacketHandler(5, 10));byte[] packet2 = helper2.processPacket(data);if (packet2 != null) {System.out.println(new String(packet2)); // 输出: Hello}// 3. 使用固定长度PacketHelper helper3 = new PacketHelper(new FixedLengthPacketHandler(5));byte[] packet3 = helper3.processPacket(data);if (packet3 != null) {System.out.println(new String(packet3)); // 输出: START}// 4. 使用可变长度byte[] variableData = new byte[] {0x00, 0x05, 'H', 'e', 'l', 'l', 'o'};PacketHelper helper4 = new PacketHelper(new VariableLengthPacketHandler(0, 2));byte[] packet4 = helper4.processPacket(variableData);if (packet4 != null) {System.out.println(new String(packet4)); // 输出: Hello}}
}

5. 总结
重新整理后的代码结构清晰,功能完善,支持以下粘包处理策略:

不处理粘包:直接返回原始数据。

**不同的开始字符和结束字符:**通过 DelimitedPacketHandler 实现。

从指定位置到指定位置的截取:通过 RangePacketHandler 实现。

固定长度:通过 FixedLengthPacketHandler 实现。

**可变长度:**通过 VariableLengthPacketHandler 实现。

用户可以根据实际需求选择合适的策略,并通过 PacketHelper 统一调用。

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

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

相关文章

【C++11】移动语义

回顾 const int c的c是可以被取地址的&#xff0c;尽管是常量。所以以是否为常量来判断是否为右值是错误的。 左值与右值正确的区分方法是是否能够被取地址。&#xff08;能被取地址也就代表着是一个持久状态&#xff0c;即有持久的存储空间的值&#xff09; 常见的左值有我们…

LangChain教程 - Agent -之 ZERO_SHOT_REACT_DESCRIPTION

在构建智能 AI 助手时&#xff0c;我们希望模型能够智能地调用工具&#xff0c;以便提供准确的信息。LangChain 提供了 AgentType.ZERO_SHOT_REACT_DESCRIPTION&#xff0c;它结合了 ReAct&#xff08;Reasoning Acting&#xff09;策略&#xff0c;使得 LLM 可以基于工具的描…

移动Android和IOS自动化中常见问题

APP测试逻辑 在app编写自动化测试用例时&#xff0c;通常会出现只是简单的点点点过程&#xff0c;然而却忽略了在实际的自动化实现过程中&#xff0c;软件是对app元素的判断来执行测试脚本。所以会出现在后期已经写好自动化脚本之后还会对测试用例的更新。 App在测试时&#…

python高效试用17---两个字符串组成一个新的字符串和两个字符串组成元组作为key哪个更高效

在 Python 中&#xff0c;使用字符串连接 (str1 str2) 作为 key 和使用元组 ((str1, str2)) 作为 key 的效率差异&#xff0c;主要受以下因素影响&#xff1a; 哈希计算速度&#xff1a; 字符串连接 (str1 str2)&#xff1a;会创建一个新的字符串对象&#xff0c;并计算哈希…

深入浅出Java try-with-resources:告别资源泄漏的烦恼

一、为什么需要try-with-resources&#xff1f; 在Java开发中&#xff0c;我们经常需要处理各种资源&#xff1a;文件流、数据库连接、网络套接字等。这些资源都有一个共同特点——必须在使用后正确关闭。传统的资源管理方式存在三大痛点&#xff1a; 代码臃肿&#xff1a;每…

Python+DeepSeek:开启AI编程新次元——从自动化到智能创造的实战指南

文章核心价值 技术热点:结合全球最流行的编程语言与国产顶尖AI模型实用场景:覆盖代码开发/数据分析/办公自动化等高频需求流量密码:揭秘大模型在编程中的创造性应用目录结构 环境搭建:5分钟快速接入DeepSeek场景一:AI辅助代码开发(智能补全+调试)场景二:数据分析超级助…

Linux tcpdump -any抓的包转换成标准的pcap

在 Linux 中使用 tcpdump -any 抓包并转换为标准 pcap 文件时出现额外字段,通常与 链路层协议头部的差异 以及 pcap 文件格式的兼容性 有关。以下是详细原因和解决方案: 一、问题原因分析 -any 选项的局限性 tcpdump -any 会自动猜测链路层协议类型(如 Ethernet、IEEE 802…

【SpringMVC】深入解析使用 Postman 在请求中传递对象类型、数组类型、参数类型的参数方法和后端参数重命名、及非必传参数设置的方法

SpringMVC—请求传参 1. 传递对象 如果参数比较多时&#xff0c;方法声明就需要有很多形参&#xff1b;并且后续每次新增一个参数&#xff0c;也需要修改方法声明. 我们不妨把这些参数封装为一个对象&#xff1b; Spring MVC 也可以自动实现对象参数的赋值&#xff0c;比如 Us…

一个差劲的软件设计

项目概况&#xff1a; 之前自己设计并开发了一个用C#开发的上位机软件&#xff0c;整个软件只有一个Form&#xff0c;一个TabControl&#xff0c;3个TabControlPanel&#xff0c;总共100多个lable、textbox、ListBox等控件都放在这3个TabControlPanel里。 问题&#xff1a; 1.…

Linux练级宝典->进程控制详解(进程替换,fork函数)

目录 进程创建 fork函数 写时拷贝 进程终止 进程退出码 exit函数 _exit函数 return&#xff0c;exit _exit之间的区别和联系 进程等待 进程等待的必要性 获取子进程status 进程等待的方法 wait waipid 多子进程创建理解 非阻塞轮询检测子进程 进程程序替换 替…

RabbitMq--消息可靠性

12.消息可靠性 1.消息丢失的情况 生产者向消息代理传递消息的过程中&#xff0c;消息丢失了消息代理&#xff08; RabbitMQ &#xff09;把消息弄丢了消费者把消息弄丢了 那怎么保证消息的可靠性呢&#xff0c;我们可以从消息丢失的情况入手——从生产者、消息代理&#xff0…

Windows中在VSCode/Cursor上通过CMake或launch文件配置CUDA编程环境

前置步骤 安装符合GPU型号的CUDA Toolkit 配置好 nvcc 环境变量 安装 Visual Studio 参考https://blog.csdn.net/Cony_14/article/details/137510909 VSCode 安装插件 Nsight Visual Studio Code Edition 注意&#xff1a;不是vscode-cudacpp。若两个插件同时安装&#xff0c;…

Spark(8)配置Hadoop集群环境-使用脚本命令实现集群文件同步

一.hadoop的运行模式 二.scp命令————基本使用 三.scp命令———拓展使用 四.rsync远程同步 五.xsync脚本集群之间的同步 一.hadoop的运行模式 hadoop一共有如下三种运行方式&#xff1a; 1. 本地运行。数据存储在linux本地&#xff0c;测试偶尔用一下。我们上一节课使用…

聚焦两会:科技与发展并进,赛逸展2025成创新新舞台

在十四届全国人大三次会议和全国政协十四届三次会议期间&#xff0c;代表委员们围绕多个关键议题展开深入讨论&#xff0c;为国家未来发展谋篇布局。其中&#xff0c;技术竞争加剧与经济转型需求成为两会焦点&#xff0c;将在首都北京举办的2025第七届亚洲消费电子技术贸易展&a…

【音视频】ffmpeg命令提取像素格式

1、提取YUV数据 提取yuv数据&#xff0c;并保持分辨率与原视频一致 使用-pix_fmt或-pixel_format指定yuv格式提取数据&#xff0c;并保持原来的分辨率 ffmpeg -i music.mp4 -t "01:00" -pixel_format yuv420p music.yuv提取成功后&#xff0c;可以使用ffplay指定y…

【从零开始学习计算机科学】计算机体系结构(二)指令级并行(ILP)

【从零开始学习计算机科学】【从零开始学习计算机科学】计算机体系结构(二)指令级并行(ILP) ILP流水线(pipeline)流水线调度循环展开和循环流水循环展开。循环展开的具体步骤可以描述为,软件流水(循环流水)。我们可以通过流水线的思想处理循环的执行,即不需要这一次的…

android edittext 防止输入多个小数点或负号

有些英文系统的输入法,或者定制输入法。使用xml限制不了输入多个小数点和多个负号。所以代码来控制。 一、通过XML设置限制 <EditTextandroid:id="@+id/editTextNumber"android:layout_width="wrap_content"android:layout_height="wrap_conten…

2019年蓝桥杯第十届CC++大学B组真题及代码

目录 1A&#xff1a;组队&#xff08;填空5分_手算&#xff09; 2B&#xff1a;年号字符&#xff08;填空5分_进制&#xff09; 3C&#xff1a;数列求值&#xff08;填空10分_枚举&#xff09; 4D&#xff1a;数的分解&#xff08;填空10分&#xff09; 5E&#xff1a;迷宫…

从C#中的MemberwiseClone()浅拷贝说起

MemberwiseClone() 是 C# 中的一个方法&#xff0c;用于创建当前对象的浅拷贝&#xff08;shallow copy&#xff09;。它属于 System.Object 类&#xff0c;因此所有 C# 对象都可以调用该方法。 1. MemberwiseClone() 的含义 浅拷贝&#xff1a;MemberwiseClone() 会创建一个新…

笔记六:单链表链表介绍与模拟实现

在他一生中&#xff0c;从来没有人能够像你们这样&#xff0c;以他的视角看待这个世界。 ---------《寻找天堂》 目录 文章目录 一、什么是链表&#xff1f; 二、为什么要使用链表&#xff1f; 三、 单链表介绍与使用 3.1 单链表 3.1.1 创建单链表节点 3.1.2 单链表的头插、…