一。序列化器 (对象序列化,反序列化)
序列化反序列号其实是java对象类型和ByteBuf类型或者byte[]类型的互相转换( io.netty.buffer.ByteBuf;)
1、kryo方案序列化
<!--kryo序列号器-->
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.42</version>
</dependency>
KryoFactory 用来创建Kryo 对象
package com.test.netty.kryocodec;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.serializers.DefaultSerializers;
import de.javakaffee.kryoserializers.*;
import java.lang.reflect.InvocationHandler;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
public class KryoFactory {
public static Kryo createKryo() {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer());
kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer());
kryo.register(InvocationHandler.class, new JdkProxySerializer());
kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
kryo.register(Pattern.class, new RegexSerializer());
kryo.register(BitSet.class, new BitSetSerializer());
kryo.register(URI.class, new URISerializer());
kryo.register(UUID.class, new UUIDSerializer());
UnmodifiableCollectionsSerializer.registerSerializers(kryo);
SynchronizedCollectionsSerializer.registerSerializers(kryo);
kryo.register(HashMap.class);
kryo.register(ArrayList.class);
kryo.register(LinkedList.class);
kryo.register(HashSet.class);
kryo.register(TreeSet.class);
kryo.register(Hashtable.class);
kryo.register(Date.class);
kryo.register(Calendar.class);
kryo.register(ConcurrentHashMap.class);
kryo.register(SimpleDateFormat.class);
kryo.register(GregorianCalendar.class);
kryo.register(Vector.class);
kryo.register(BitSet.class);
kryo.register(StringBuffer.class);
kryo.register(StringBuilder.class);
kryo.register(Object.class);
kryo.register(Object[].class);
kryo.register(String[].class);
kryo.register(byte[].class);
kryo.register(char[].class);
kryo.register(int[].class);
kryo.register(float[].class);
kryo.register(double[].class);
return kryo;
}
}
KryoSerializerUtil 序列化反序列化工具类
package com.test.netty.kryocodec;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class KryoSerializerUtil {
private static Kryo kryo = KryoFactory.createKryo();
/**
* 对象转成ByteBuf
*
* @param object 对象
* @param byteBuf ByteBuf
*/
public static void serialize(Object object, ByteBuf byteBuf) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos);
kryo.writeClassAndObject(output, object);
output.flush();
output.close();
byte[] b = baos.toByteArray();
try {
baos.flush();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
byteBuf.writeBytes(b);
}
/**
* ByteBuf转成对象
*
* @param byteBuf ByteBuf
* @return 对象
*/
public static Object deserialize(ByteBuf byteBuf) {
if (byteBuf == null) {
return null;
}
Input input = new Input(new ByteBufInputStream(byteBuf));
return kryo.readClassAndObject(input);
}
}
2、protostuff序列化
<!-- protostuff-->
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-api</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.10</version>
</dependency>
二。编码器解码器,其实就是序列化器和netty集成起来
一个继承MessageToByteEncoder
一个继承ByteToMessageDecoder
//编码器 将对象转为字节
public class KryoEncoder extends MessageToByteEncoder<MyMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, MyMessage message, byteBuf out) throws Exception {
KryoSerializer.serialize(message, out);
ctx.flush();
}
}
//解码器 将字节转为对象
public class KryoDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object obj = KryoSerializer.deserialize(in);
out.add(obj);
}
}
三。base64的编码与解码
//方法1:cn.hutool
//编码
String str = "abc";
String base64Str = Base64Encoder.encode(str.getBytes());
System.out.println(base64Str);
//解码
str = new String(Base64Decoder.decode(base64Str), StandardCharsets.UTF_8);
System.out.println(str);
//方法2:google的guava
//编码
String temp = "abcsd";
String base64Str = BaseEncoding.base64().encode(temp.getBytes());
System.out.println(base64Str);
//解码
temp = new String(BaseEncoding.base64().decode(base64Str), StandardCharsets.UTF_8);
System.out.println(temp);
四。BCryptPasswordEncoder
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author ccp24 on 2017/12/8 14:45
*/
public class EncryptUtil {
/**
* 密码加密
*
* @param password 原始密码
* @return 加密后的密码
*/
public static String encryptPassword(String password) {
BCryptPasswordEncoder crypt = new BCryptPasswordEncoder();
return crypt.encode(password);
}
/**
* 密码匹配
*
* @param rawPassword 未加密密码
* @param encodedPassword 加密密码
* @return 如果匹配一致,返回true。否则返回false
*/
public static boolean matchPassword(String rawPassword, String encodedPassword) {
BCryptPasswordEncoder crypt = new BCryptPasswordEncoder();
return crypt.matches(rawPassword, encodedPassword);
}
}
把密码123456转成 BCrypt格式
orgOperationUserDO.setPassword(EncryptUtil.encryptPassword(“123456”));
五。根据星云通讯项目测试
Arrays.asList 把数组转成集合
concatAll 把二进制集合转成一个字节数组
package com.king.bytetest;
import com.king.bytetest.util.BitOperator;
import com.king.bytetest.util.ByteUtil;
import java.util.Arrays;
/**
* @Author lhc
* @Date 2020-09-28 10:00
**/
public class Test {
/**
* @Author lhc
* @Date 2020-09-27 18:15
**/
public static void main(String[] args) {
Charge charge = new Charge();
charge.setReverd1(3);
charge.setAge(20);
charge.setName("chargeName");
charge.setColor("bule");
byte[] bytes = javaObjectChangeToByteArray(charge);
Charge charge1 = byteArrayChangeToObject(bytes);
System.out.println(charge1);
}
/**
* java对象转成字节数组
*
* @param charge java对象
* @return 字节数组
*/
public static byte[] javaObjectChangeToByteArray(Charge charge) {
byte[] msgBody = BitOperator.concatAll(Arrays.asList(
//预留1
BitOperator.integerToBytes(charge.getReverd1(), 1),
//年龄
BitOperator.integerToBytes(charge.getAge(), 2),
//名称
ByteUtil.stringToByteByASCII(charge.getName(), 32),
//颜色
ByteUtil.stringToByteByASCII(charge.getColor(), 4)
));
return msgBody;
}
/**
* 字节数组转成java对象
*
* @param byteArray
* @return java对象
*/
public static Charge byteArrayChangeToObject(byte[] byteArray) {
Charge charge = new Charge();
// 1.预留1
int reverd1 = ByteUtil.intFromBytes(byteArray, 0, 1);
charge.setReverd1(ByteUtil.intFromBytes(byteArray, 0, 1));
// 2.年龄
int age = ByteUtil.intFromBytes(byteArray, 1, 2);
charge.setAge(ByteUtil.intFromBytes(byteArray, 1, 2));
// 3.名称
String name = ByteUtil.strFromBytesByASCII(byteArray, 3, 32);
charge.setName(ByteUtil.strFromBytesByASCII(byteArray, 3, 32));
// 4.颜色
String color = ByteUtil.strFromBytesByASCII(byteArray, 35, 4);
charge.setColor(ByteUtil.strFromBytesByASCII(byteArray, 35, 4));
return charge;
}
}
六。星云接收充电桩消息 根据协议把二进制数据转成java对象
协议背景:通信数据报⽂采⽤⼆进制格式,把传过来的二进制数据根据协议转为Java对象
1. 根据如下截图创建java对象,见下面类CNTEPackageHeadData
2. 转换,见下面
1.根据如上截图创建java对象
@Data
public class CNTEPackageHeadData {
/**
* 起始域 2个字节
*/
private int originateId;
/**
* 长度 2个字节
*/
private int length;
/**
* 信息域 1个字节
*/
private int infoDomain;
/**
* 序列号域 1个字节
*/
private int serialNumber;
/**
* 命令ID
*/
private int commandId;
/**
* 流水ID 协议是数据域校验和域
*/
private int flowId;
}
2.把二进制数据根据如上截图内容转成java对象
private static CNTEPackageHeadData parseHeaderFromBytes(byte[] data) {
CNTEPackageHeadData headData = new CNTEPackageHeadData();
// 1.起始域
headData.setOriginateId(ByteUtil.intFromBytes(data, 0, 2));
// 2.长度
headData.setLength(ByteUtil.intFromBytes(data, 2, 2));
// 3.信息域
headData.setInfoDomain(ByteUtil.intFromBytes(data, 4, 1));
// 4.序列号域
headData.setSerialNumber(ByteUtil.intFromBytes(data, 5, 1));
// 5.命令代码
headData.setCommandId(ByteUtil.intFromBytes(data, 6, 2));
return headData;
}
ByteUtil见有道云笔记——星云——通讯项目与协议
七。星云发送消息给充电桩 java对象转成二进制
1.根据协议创建一个java对象 例如上面的类CNTEPackageHeadData
2.new CNTEPackageHeadData() 并把信息set属性进去, 如setCommandId(108)
3.把CNTEPackageHeadData对象转成二进制数据(字节数组类型)
4.发送
java对象转成字节数组(二进制对象)
/**
* 服务器应答充电桩上报充电信息报文
*
* @param bodyData 数据消息体
* @param flowId 流水号
* @return 服务器应答充电桩上报充电信息报文数据
*/
public static byte[] encode4ServerReportChargingRecordCmdMsg(ServerReportChargingRecordBody bodyData, int flowId) {
byte[] msgBody = BitOperator.concatAll(Arrays.asList(
//预留1 (2)
BitOperator.integerToBytes(bodyData.getReserved1(), 2),
//预留1 (4)
BitOperator.integerToBytes(bodyData.getReserved2(), 2),
//充电枪口
BitOperator.integerToBytes(bodyData.getChargingGunsNumber(), 1),
//用户卡号/用户识别号 (32)
ByteUtil.stringToByteByASCII(bodyData.getChargingCardNumber(), 32),
//内部索引号
BitOperator.integerToBytes(bodyData.getInternalIndexNumber(), 4),
//字段有效标志 (1)
BitOperator.integerToBytes(bodyData.getFieldValidFlag(), 1),
//充电优惠前金额 (4)
BitOperator.integerToBytes(bodyData.getChargingAmount(), 4),
//充电折扣金额 (4)
BitOperator.integerToBytes(bodyData.getChargingDiscountAmount(), 4),
//充电实扣金额 (4)
BitOperator.integerToBytes(bodyData.getChargingDeductionAmount(), 4)
));
byte[] msgHeader = generateMsgHeader(InstructionCodeConsts.msg_id_terminal_report_charging_record, msgBody, flowId);
byte[] headerAndBody = BitOperator.concatAll(msgHeader, msgBody);
// 校验码
int checkSum = BitOperator.getCRC16CheckSum(headerAndBody, 0, headerAndBody.length);
// 连接并且转义
return doEncode(headerAndBody, checkSum);
}
八。unicode编码解码在线工具
九。为null的字段不序列化 @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BusinessDetailsVO {
private String name;
private String website;
private ImageVO logo;
}