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.pswp.cn/web/71897.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71897.shtml
英文地址,請注明出處:http://en.pswp.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 單鏈表的頭插、…