Socket:半包及粘包的一种处理方法

  1. 先说下思路:
  • 当出现半包情况时,原本一整段的消息被分成两部分或多部分,导致用来判断消息是否完整的函数无法判断,所以这时候就将先到达的内容存储起来,用于与后到达的内容连接在一起
  • 当出现粘包情况时,消息A和消息B紧密的连接在一起,这就导致处理消息的函数如果不将消息分开,就很容易导致消息丢失或者程序卡在这里。所以就根据数据头中的长度,判断收到的消息的长度是否一致,如果消息长度比数据头中记录的长度大,就将粘包的两个消息分开
  1. 首先是建立客户端监听服务端消息的函数:
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());
            }
        }
    }
  1. 对数据头进行判断。(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对象中的数据进行解析处理
    }
  1. 标识头检查:
private boolean detectionDataSign(int[] arr){
        if(arr[0] != 67 && arr[1] != 66){
            arr_storage = null;
            return false;
        }
        return true;
    }
  1. 数据长度检查:
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;
    }
  1. 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;
    }```

猜你喜欢

转载自blog.csdn.net/weixin_37378399/article/details/83013365