- 先说下思路:
- 当出现半包情况时,原本一整段的消息被分成两部分或多部分,导致用来判断消息是否完整的函数无法判断,所以这时候就将先到达的内容存储起来,用于与后到达的内容连接在一起。
- 当出现粘包情况时,消息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;
}```