Socket:半包及粘包的一種處理方法
阿新 • • 發佈:2018-11-03
- 先說下思路:
- 當出現半包情況時,原本一整段的訊息被分成兩部分或多部分,導致用來判斷訊息是否完整的函式無法判斷,所以這時候就將先到達的內容儲存起來,用於與後到達的內容連線在一起。
- 當出現粘包情況時,訊息A和訊息B緊密的連線在一起,這就導致處理訊息的函式如果不將訊息分開,就很容易導致訊息丟失或者程式卡在這裡。所以就根據資料頭中的長度,判斷收到的訊息的長度是否一致,如果訊息長度比資料頭中記錄的長度大,就將粘包的兩個訊息分開。
- 首先是建立客戶端監聽服務端訊息的函式:
private void getDataFormServer(){ isRun = true; while(isRun){ try{ // 建立輸入流物件InputStream is = socket.getInputStream(); int length = is.available(); //獲取訊息長度 // 建立一個數組,將is中的位元組流輸入到這個陣列中 int[] arr = new int[length]; for(int i=0; i<length; i++){ arr[i] = is.read(); } if(length > 0){ //半包情況處理:將這次的資料和儲存的資料加在一起 if(arr_storage != null){ arr = add2intArray2oneArray(arr_storage, arr); } //對資訊進行判斷及處理 detectionDataHead(arr); } }catch (Exception e){ isRun = false; //writeLogToFile(String string):測試的時候將資訊輸出到一個日誌檔案中 writeLogToFile("get Data is fail! " + e.toString()); } } }
- 對資料頭進行判斷。(PS:我設定的資料頭:兩位識別符號+4位CRC+4位資料長度)
private void detectionDataHead(int[] arr){ //識別符號檢查:請看第4點 if(!detectionDataSign(arr)){ writeLogToFile("識別符號錯誤"); return; } //資料長度檢查:請看第5點 if(!detectionDataLength(arr)){ return; } //CRC檢查:請看第6點 if(!detectionDataCrc(arr)){ return; } //資料無誤 arr_storage = null; //接下來就是對正確的訊息進行處理。比如我的: JSONObject jsonObject = serverData2JsonObject(arr); //將訊息轉換成json物件 detectionJsonObject(jsonObject); //這是對json物件中的資料進行解析處理 }
- 標識頭檢查:
private boolean detectionDataSign(int[] arr){
if(arr[0] != 67 && arr[1] != 66){
arr_storage = null;
return false;
}
return true;
}
- 資料長度檢查:
private boolean detectionDataLength(int[] arr){ int length = 0; //計算資料長度 for(int i=6; i<10; i++){ length *= 256; /*因為記錄長度使用十六進位制記錄的,但是int中變成了十進位制,所以要轉換*/ length += arr[i]; } //如果 訊息長度 == 資料長度 + 10,代表沒問題 if(arr.length == length+10){ return true; } //出現 半包問題 else if(arr.length < length+10){ arr_storage = arr; //arr_storage 是設定的用來儲存訊息的int陣列 return false; } //出現 粘包問題 else if(arr.length > length+10){ //將資料一分為二,分別進行訊息判斷,看看是否是完整的資料。 int[] arr1 = new int[length + 10]; int[] arr2 = new int[length - arr.length]; for(int i = 0; i < length + 10; i++){ arr1[i] = arr[i]; } for(int j = length; j<arr.length; j++){ arr2[j] = arr[j]; } detectionDataHead(arr1); detectionDataHead(arr2); } return false; }
- CRC檢查:
private boolean detectionDataCrc(int[] arr){
int crc = 0;
/*因為訊息中記錄CRC時使用十六進位制記錄的,但是int中變成了十進位制,所以要轉換。*/
for(int i=2; i<6; i++){
crc *= 256;
crc += arr[i];
}
for(int j = 10; j < arr.length; j++){
crc -= arr[j];
}
if(crc == 0)
return true;
writeLogToFile("CRC 錯誤");
return false;
}```