package com.zy.common.utils; import com.zy.common.model.annotations.*; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.*; /** * Tcp协议报文基类 * @author vincent */ public class Struct implements java.io.Serializable { private transient Map decodeByteMapping = new HashMap<>(); private transient Map encodeByteMapping = new HashMap<>(); // 解码前的原生字节数组 private transient byte[] dataBytes = null; private transient byte[] decodeBytes = null; private transient byte[] encodeBytes = null; private transient byte[] unusedBytes = null; @SuppressWarnings({"unchecked", "rawtypes"}) public static T decode(Class cls, byte[] bytes) throws Exception { Struct instance = cls.newInstance(); return instance.decode(bytes); } // 字节数组 ===>> 转为十六进制字符串 public static String toHex(byte[] bytes) { StringBuilder buf = new StringBuilder(bytes.length * 2); for (byte b : bytes) { // 使用String的format方法进行转换 buf.append(String.format("%02x", b & 0xff)); } return buf.toString().toUpperCase(); } public static byte[] toBytes(String str) { if (str == null || str.trim().equals("")) return new byte[0]; byte[] bytes = new byte[str.length() / 2]; for (int i = 0; i < str.length() / 2; i++) { String subStr = str.substring(i * 2, i * 2 + 2); bytes[i] = (byte) Integer.parseInt(subStr, 16); } return bytes; } /** * @功能: BCD码转为10进制串(阿拉伯数据) * @参数: BCD码 * @结果: 10进制串 */ public static String bcd2Str(byte[] bytes) { StringBuffer temp = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); temp.append((byte) (bytes[i] & 0x0f)); } return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp .toString().substring(1) : temp.toString(); } /** * @功能: 10进制串转为BCD码 * @参数: 10进制串 * @结果: BCD码 */ public static byte[] str2Bcd(String asc) { int len = asc.length(); int mod = len % 2; if (mod != 0) { asc = "0" + asc; len = asc.length(); } byte abt[] = new byte[len]; if (len >= 2) { len = len / 2; } byte bbt[] = new byte[len]; abt = asc.getBytes(); int j, k; for (int p = 0; p < asc.length() / 2; p++) { if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) { j = abt[2 * p] - '0'; } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { j = abt[2 * p] - 'a' + 0x0a; } else { j = abt[2 * p] - 'A' + 0x0a; } if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) { k = abt[2 * p + 1] - '0'; } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { k = abt[2 * p + 1] - 'a' + 0x0a; } else { k = abt[2 * p + 1] - 'A' + 0x0a; } int a = (j << 4) + k; byte b = (byte) a; bbt[p] = b; } return bbt; } public static byte getXor(byte[] datas, int start, int end) { byte temp = datas[start]; for (int i = start + 1; i < end; i++) { temp ^= datas[i]; } return temp; } /** * 求校验和的算法 * @param b 需要求校验和的字节数组 * @return 校验和 */ public static byte sumCheck(byte[] b, int start, int end) { int sum = 0; for (int i = start; i < end; i++) { sum = sum + b[i]; } return (byte) (sum & 0xff); } //字符串转换为ascii public static byte[] StrToAsc(String content) { try { return content.getBytes(StandardCharsets.US_ASCII); } catch (Exception e) { e.printStackTrace(); return null; } } //ascii转换为string public static String AscToStr(byte[] bytes) { StringBuffer sbu = new StringBuffer(); for (byte aByte : bytes) { sbu.append(Character.toString((char) aByte)); } return sbu.toString(); } public Class getClassType(String name) { return null; } /** * 判断大小端 * @return true: 小端 / false: 大端 */ public boolean isReverse() { return this.getClass().getAnnotation(little.class) != null; } /** * 获取字节数组 */ public byte[] toBytes() throws Exception { return encode(this); } public Map toDecodeHexMapping() throws Exception { if (this.decodeByteMapping == null) this.decodeByteMapping = new HashMap<>(); Map mapping = getHex(this.decodeByteMapping); if (this.decodeBytes != null) { mapping.put("byte[]", Struct.toHex(this.decodeBytes)); } return mapping; } public Map toEncodeHexMapping() throws Exception { if (this.encodeBytes == null) this.encode(this); if (this.encodeByteMapping == null) this.encodeByteMapping = new HashMap<>(); Map mapping = getHex(this.encodeByteMapping); mapping.put("byte[]", Struct.toHex(this.encodeBytes)); return mapping; } public Map getHex(Map bytemapping) throws Exception { Map map = new LinkedHashMap<>(); Field[] fields = this.getClass().getFields(); for (Field field : fields) { byte[] bytes = bytemapping.get(field.getName()); if (bytes != null) { map.put(field.getName(), Struct.toHex(bytes)); } } return map; } // 转java对象到数组 public byte[] encode(Struct entity) throws Exception { ByteArrayOutputStream baOs = new ByteArrayOutputStream(); write(new DataOutputStream(baOs), entity); entity.encodeBytes = baOs.toByteArray(); return entity.encodeBytes; } /** * 写数据到输出流 * * @param dos * @param entity */ public void write(DataOutputStream dos, Struct entity) throws Exception { if (entity == null) return; if (entity.encodeByteMapping == null) entity.encodeByteMapping = new HashMap<>(); Field[] fields = entity.getClass().getFields(); EvaluationContext context = new StandardEvaluationContext(); for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { field.setAccessible(true); String name = field.getName(); Class type = field.getType(); String typeName = type.getSimpleName(); if (typeName.equals("Struct")) { if (entity.getClassType(name) != null) { type = entity.getClassType(name); typeName = type.getSimpleName(); } else if (field.get(entity) != null) { type = field.get(entity).getClass(); typeName = type.getSimpleName(); } } size size = field.getAnnotation(size.class); bit bit = field.getAnnotation(bit.class); expr expr = field.getAnnotation(expr.class); flag flag = field.getAnnotation(flag.class); if (size == null && bit == null && expr == null) { continue; } int bited = 0;//已写位数 int length = 0;//定义的长度 if (size != null) { length = size.value(); bited += size.value() * 8; } else if (bit != null) { //按位写方式未实现处理 throw new RuntimeException("按位写方式未实现处理"); } else if (expr != null) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expr.value()); Integer len = null; try { len = CommonUtils.parseInt(exp.getValue(context, entity)); } catch (Exception e) { len = 0; } if (len != null) { if (len == -1) continue; length = len; bited += len * 8; } } context.setVariable(name, field.get(entity)); byte[] bytes = new byte[length]; if (typeName.equals("int") || typeName.equals("Integer")) { dos.write(bytes = convert(length, field.getInt(entity))); } else if (typeName.equals("long") || typeName.equals("Long")) { dos.write(bytes = convert(length, field.getLong(entity))); } else if (typeName.equals("short") || typeName.equals("Short")) { dos.write(bytes = convert(length, field.getShort(entity))); } else if (typeName.equals("byte") || typeName.equals("Byte")) { dos.write(bytes = convert(length, field.getByte(entity))); } else if (typeName.equals("String")) { String str = (String) field.get(entity); if (str == null) str = ""; str = str.replaceAll(" ", " "); byte[] bts = null; if (flag != null && flag.value().equals("ASCII")) { bts = Struct.StrToAsc(str); } else if (flag != null && flag.value().equals("BCD")) { bts = Struct.str2Bcd(str); } else { bts = str.getBytes(); } if (length == 0) {//如果长度未设置,以当前实际的字节为准 bytes = new byte[bts.length]; } System.arraycopy(bts, 0, bytes, 0, bts.length); dos.write(bytes); } else if (typeName.equals("byte[]")) { byte[] bts = (byte[]) field.get(entity); if (bts != null) { if (length == 0) {//如果长度未设置,以当前实际的字节为准 bytes = new byte[bts.length]; } System.arraycopy(bts, 0, bytes, 0, bts.length); dos.write(bytes); } } else if (type.isArray()) { ByteArrayOutputStream baOs1 = new ByteArrayOutputStream(); DataOutputStream dos1 = new DataOutputStream(baOs1); for (Object object : (Object[]) field.get(entity)) { write(dos1, (Struct) object); } byte[] bts = baOs1.toByteArray(); if (length == 0) {//如果长度未设置,以当前实际的字节为准 bytes = new byte[bts.length]; } System.arraycopy(bts, 0, bytes, 0, bts.length); dos.write(bytes); } else { byte[] bts = encode((Struct) field.get(entity)); if (length == 0) {//如果长度未设置,以当前实际的字节为准 bytes = new byte[bts.length]; } System.arraycopy(bts, 0, bytes, 0, bts.length); dos.write(bytes); } entity.encodeByteMapping.put(name, bytes); } } public byte[] convert(int size, long value) throws Exception { ByteArrayOutputStream baOs = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baOs); switch (size) { case 1://byte dos.write((byte) value); break; case 2://short dos.writeShort(this.isReverse() ? Short.reverseBytes((short) value) : (short) value); break; case 4://int dos.writeInt(this.isReverse() ? Integer.reverseBytes((int) value) : (int) value); break; case 8://long dos.writeLong(this.isReverse() ? Long.reverseBytes((long) value) : (long) value); break; } return baOs.toByteArray(); } public long convertEx(long value, int size) throws Exception { switch (size) { case 1://byte return value; case 2://short return (this.isReverse() ? Short.reverseBytes((short) value) : (short) value); case 4://int return (this.isReverse() ? Integer.reverseBytes((int) value) : (int) value); case 8://long return (this.isReverse() ? Long.reverseBytes((long) value) : (long) value); } return 0; } /** * 解码: 字节数组 ====>> java对象 */ @SuppressWarnings({"unchecked", "rawtypes"}) public T decode(byte[] bytes) throws Exception { this.dataBytes = bytes; Struct instance = this; Class cls = this.getClass(); Field[] fields = cls.getFields(); if (this.decodeByteMapping == null) instance.decodeByteMapping = new HashMap(); // spring el表达式 EvaluationContext context = new StandardEvaluationContext(); context.setVariable("_", bytes.length); int bited = 0;//已读位数 for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { size size = field.getAnnotation(size.class); bit bit = field.getAnnotation(bit.class); expr expr = field.getAnnotation(expr.class); flag flag = field.getAnnotation(flag.class); // 没有size、bit、expr注解的变量不进行解析 if (size == null && bit == null && expr == null) { continue; } String name = field.getName(); Class type = field.getType(); String typeName = type.getSimpleName(); if (typeName.equals("Struct")) { if (instance.getClassType(name) != null) { type = instance.getClassType(name); typeName = type.getSimpleName(); } } context.setVariable("__", bytes.length - bited / 8); byte[] bts = null; if (size != null) { bts = new byte[size.value()]; System.arraycopy(bytes, bited / 8, bts, 0, size.value()); bited += size.value() * 8; } else if (bit != null) { int val = 0; String strBin = ""; for (int i = bited; i < bited + bit.value(); i++) {//逐位扫描 if ((bytes[i / 8] & (1 << (i % 8))) != 0) {//如果位为1 strBin += "1"; } else { strBin += "0"; } val = Integer.parseInt(strBin, 2); } byte[] result = new byte[4]; result[3] = (byte) (val); result[2] = (byte) (val >> 8); result[1] = (byte) (val >> 16); result[0] = (byte) (val >> 24); bts = new byte[(int) Math.ceil((double) bit.value() / 8.0)]; System.arraycopy(result, result.length - bts.length, bts, 0, bts.length); bited += bit.value(); } else if (expr != null) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expr.value()); Integer len = CommonUtils.parseInt(exp.getValue(context, instance)); if (len != null) { if (len == -1) continue; bts = new byte[len]; System.arraycopy(bytes, bited / 8, bts, 0, len); bited += len * 8; } } if (bts.length == 0) continue; DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bts)); if (typeName.equals("int") || typeName.equals("Integer")) { field.setInt(instance, (int) convertEx(dis.readInt(), 4)); } else if (typeName.equals("long") || typeName.equals("Long")) { field.setLong(instance, convertEx(dis.readLong(), 8)); } else if (typeName.equals("short") || typeName.equals("Short")) { field.setShort(instance, (short) convertEx(dis.readShort(), 2)); } else if (typeName.equals("byte") || typeName.equals("Byte")) { field.setByte(instance, (byte) convertEx(dis.readByte(), 1)); } else if (typeName.equals("byte[]")) { field.set(instance, bts); } else if (typeName.equals("String")) { String strStr = new String(bts, StandardCharsets.UTF_8); if (flag != null && flag.value().equals("BCD")) { strStr = bcd2Str(bts); } else if (flag != null && flag.value().equals("ASCII")) { strStr = Struct.AscToStr(bts); } strStr = strStr.replaceAll("[\u0000]", ""); field.set(instance, strStr); } else if (type.isArray()) { byte[] arrayBytes = bts; List list = new ArrayList(); while (arrayBytes.length > 0 && list.size() < 256) { Struct item = decode((Class) type.getComponentType(), arrayBytes); list.add(item); arrayBytes = item.unusedBytes; } Object array = Array.newInstance(type.getComponentType(), list.size()); for (int i = 0; i < list.size(); i++) { Array.set(array, i, list.get(i)); } field.set(instance, array); } else if (type == Struct.class || Struct.class.isAssignableFrom(type)) { byte[] arrayBytes = bts; Struct item = decode((Class) type, arrayBytes); field.set(instance, item); } else { throw new RuntimeException(cls.getSimpleName() + "字段" + field.getName() + "类型未知"); } instance.decodeByteMapping.put(name, bts); context.setVariable(name, field.get(instance)); } byte[] useBytes = new byte[bited / 8]; System.arraycopy(bytes, 0, useBytes, 0, bited / 8); instance.decodeBytes = useBytes; byte[] unusedBytes = new byte[bytes.length - (bited / 8)]; if (unusedBytes.length > 0) { System.arraycopy(bytes, useBytes.length, unusedBytes, 0, unusedBytes.length); } instance.unusedBytes = unusedBytes; return (T) instance; } public static class TestMessage { public @size(1) byte begin = 0x7e;//起始符 public @size(2) short type = 0;//消息ID public @bit(2) byte retain;//消息体属性>保留 public @bit(1) byte subpkg;//消息体属性>是否分包,0表示不分包 public @bit(3) byte encrypt;//消息体属性>加密方式,0表示不加密 public @bit(10) short length;//消息体长度 public @size(6) @flag("BCD") String number;//手机号码 public @size(2) short serial;//消息流水号 public @expr("#subpkg==1?2:0") short pkgs;//分包数 public @expr("#subpkg==1?2:0") short index;//包序号 public @expr("#length") byte[] data;//数据部分 public @size(1) byte verify;//校验码 public @size(1) byte end = 0x7e;//结束符 } }