Netty 跨平台序列化 Java解析C结构体最佳实践之一 对象配置

  最佳实践当然是使用框架啦, Nettyx提供了史上最快的C 结构体 序列化/反序列化器, 完爆阿里fury !!!

直接上依赖:

请从maven中央仓获取{lastest.version},最新版本号
<dependency>
    <groupId>io.github.fbbzl</groupId>
    <artifactId>nettyx</artifactId>
    <version>{lastest.version}</version>
</dependency>

概述

  Nettyx是基于netty 4.1.X.Final版本二次封装的框架,扩展了一些工具, 其中最重要的功能便是跨平台序列化, 接下来将展示如何接收并解析成C的结构体.


  想要定义struct, 必须先从基础类型开始, Nettyx预定义了相当一部分的C/C++的基础类型, 例如Cint,Cfloat,Cuint等基础类型.
此处为了演示效果, 将展示如何自定义基础类型, 当Nettyx预定义的基础类型无法满足需求时, 各位即可根据这个例子自行扩展基础类型


那就先从 定义C中的long类型开始吧.

C语言中的long,一般用作用户id, 序列号什么的

import org.fz.nettyx.serializer.typed.Basic;

/**
 * 所有用来 映射C基础类型的 java类 必须实现Basic并重写对应的方法
 *  
 * @author fengbinbin
 * @since 2021-10-19 19:58
 * @apinote Clong在Nettyx已经预定义了, 此处的clong只是为了演示如何定义基础类型用, 真正开发的时候还是建议使用Nettyx内置的基础类型
 * 
 **/
public class clong extends Basic<Integer> {

    //所有basic的子类都必须提供此构造方法, 用来初始化java中的值
    public clong(Integer value) {
        super(value);
    }

	// 是否有符号, C语言中有无符号类型, 这样同样的bit位可以表示多一倍的数
	// 当然浮点数一般都是有符号的, 毕竟无符号的浮点数也没啥意义
	// 没有默认值, 所有子类必须实现
    @Override
    public boolean hasSinged() {
        return true;
    }

    // 字节顺序, C是小端, 这是和Java不一样的一个地方, 此处要注意
    // 没有提供默认的字节顺序, 所有子类必须实现
    @Override
    public ByteOrder order() {
        // C是小端, 这是另一个和Java不同的地方
        return ByteOrder.LITTLE_ENDIAN;
    }

    // 将Java中的值, 转换为ByteBuf, 所有子类必须实现
    @Override
    protected ByteBuf toByteBuf(Integer value) {
        return Unpooled.buffer(size()).writeIntLE(value);
    }

    // 将ByteBuf转换为Java中的值, 所有子类必须实现
    @Override
    protected Integer toValue(ByteBuf byteBuf) {
        return byteBuf.readIntLE();
    }

    // 此基础类型所占用的字节数量, 所有子类必须实现
    @Override
    public int size() {
        return 4;
    }
}

在定义完基础类型之后, 我们就可以开始定义struct了.仅需注意一点, 所有的struct类都需要加上@Struct注解

		
	// **所有的struct类都要加上@Struct注解!! 类似持久层框架中@Entity注解**	
    @Struct
    public class User {
	    // 用户名, 这里用了@ToCustomString这个注解, 会在之后说明如何自定义解析注解
       @ToCustomString(length=4)
        private String name;
		// clong就是上面自定义的一个基础类型
        private clong uid;
   
  		// Cchar数组
  		//数组必须使用@ToArray来指定长度
  		//Cchar为nettyx内置C基础类型之一
        @ToArray(2)
        private Cchar[] gfNames;
   		
   		//此处省略getter和getter
    }

至此, 基础类型和struct的实体类都已经编写完成.

  接下来我们进行序列化操作, Nettyx提供了StructSerializer来进行序列化和反序列化, 如果存在泛型, 可以使用TypeRefer来指定, 当然为了宣传国产框架hutool, 同样支持hutool的TypeReference

// 这里硬编码了一段字节数组, 实际上这部分数据可能来自socket, 文件等
// HexKit为Nettyx中操作16机制工具类, 如果不知道  表示方式和存储方式  的不同, 这里可能要去补习下,谁让16进制能干这么多事
byte[] bytes = HexKit.decode("122adf3547f8d65e24ff81aa3478ff4678122adf3547f8d65e24ff81aa3478ff4678");

// StructSerializer的read方法将 字节数组反序列化成struct对象, read方法有很多重载, 选择最合适的使用
User demo = StructSerializer.read(Unpooled.wrappedBuffer(bytes), User.class);
// 同样我们可以使用StructSerializer的write来,来把对象序列化成字节数组, write同样重载了很多方法, 选择最合适的使用
ByteBuf userWriteBytes = StructSerializer.write(user);

// 带泛型的话, 可以使用Nettyx的TypeRefer
TypeRefer<User<Son>> typeRefer = new TypeRefer<User<Son>>() {};
User user = StructSerializer.read(Unpooled.wrappedBuffer(bytes), typeRefer);   
ByteBuf userWriteBytes = StructSerializer.write(user, typeRefer ); 

至此我们完成了最基础的java跨平台解析C struct的操作.


自定义注解

  上面提到可以通过定义注解的方式来扩展解析逻辑, 我这波抄…借鉴了jsr303. 如果你想自定义字段的解析逻辑, 你可以实现StructPropHandler.ReadWriteHandler<A extends Annotation>来指定字段解析器, 并在泛型中指定自己定义的注解, 如下图, 我自定义了一个@ToCustomString, 并通过ToCustomStringHandler类实现了 StructPropHandler.ReadWriteHandler<ToCustomString>, 使用时只需要在指定的field上加上@ToCustomString 注解即可

@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface ToCustomString {

    /**
     * Charset string.
     * 老生常谈, 字节数组转字符需要提供字符集, 这里默认了utf-8
     * @return the legal charset
     * @see StandardCharsets
     */
    String charset() default "UTF-8";

    /**
     * Buffer length int.
     * 此字符串的字节长度
     * @return the buffer occupied by this char sequence
     */
    int bufferLength();

		/**
		* 光定义注解ToCustomString还不行, 还需要定义注解关联的解析器, 在解析器的泛型中指定ToCustomString为目标注解,
		* 更多解析器接口请至StructFieldHandler中查看
		*/
    class ToCustomStringHandler implements StructFieldHandler.ReadWriteHandler<ToCustomString> {

        @Override
        public Object doRead(StructSerializer serializer, Field field, ToCustomString toString) {
            String charset = toString.charset();
            if (!Charset.isSupported(charset)) throw new UnsupportedCharsetException("do not support charset [" + charset + "]");

            ByteBuf byteBuf = serializer.getByteBuf();
            if (!byteBuf.isReadable()) {
                throw new IllegalArgumentException(
                    "buffer is not readable please check [" + ByteBufUtil.hexDump(byteBuf) + "], field is [" + field
                        + "]");
            }
            byte[] bytes;
            byteBuf.readBytes(bytes = new byte[toString.bufferLength()]);
            return new String(bytes, Charset.forName(charset));
        }

        @Override
        public void doWrite(StructSerializer serializer, Field field, Object value, ToCustomString toString, ByteBuf writing) {
            int    bufferLength = toString.bufferLength();
            String charset      = toString.charset();

            if (value != null) writing.writeBytes(value.toString().getBytes(Charset.forName(charset)));
            else               writing.writeBytes(new byte[bufferLength]);

        }

    }
}

关于循环引用

  个人认为循环引用是模型设计的问题, StructSerializer暂时没有对循环引用进行检测,也没有规避, 模型设计时还请各位开发大佬尽量规避, 不然是会出现死循环的哟
如下图

@Data
class A {
	private clong id;
	private clong phoneNumber;
	// 第一种, 自己引用自己. 解析时会死循环
	private A selfTypeField;
	// 第二种, 循环引用. 解析时同样会死循环
	private B b;
}

@Data
class B {
   // A和B相互引用 
   private A a;
}


末尾附上Nettyx内置的C和C++语言类型, 实际开发直接使用这些内置的基础类型构建你的struct即可
在这里插入图片描述
在这里插入图片描述

包含最新版文档以及全部jar包: jar包如下 netty-buffer-4.1.32.Final-sources.jar netty-buffer-4.1.32.Final.jar netty-build-22-sources.jar netty-build-22.jar netty-codec-4.1.32.Final-sources.jar netty-codec-4.1.32.Final.jar netty-codec-http-4.1.32.Final-sources.jar netty-codec-http-4.1.32.Final.jar netty-codec-http2-4.1.32.Final-sources.jar netty-codec-http2-4.1.32.Final.jar netty-codec-memcache-4.1.32.Final-sources.jar netty-codec-memcache-4.1.32.Final.jar netty-codec-redis-4.1.32.Final-sources.jar netty-codec-redis-4.1.32.Final.jar netty-codec-socks-4.1.32.Final-sources.jar netty-codec-socks-4.1.32.Final.jar netty-codec-stomp-4.1.32.Final-sources.jar netty-codec-stomp-4.1.32.Final.jar netty-common-4.1.32.Final-sources.jar netty-common-4.1.32.Final.jar netty-example-4.1.32.Final-sources.jar netty-example-4.1.32.Final.jar netty-handler-4.1.32.Final-sources.jar netty-handler-4.1.32.Final.jar netty-handler-proxy-4.1.32.Final-sources.jar netty-handler-proxy-4.1.32.Final.jar netty-resolver-4.1.32.Final-sources.jar netty-resolver-4.1.32.Final.jar netty-tcnative-2.0.20.Final-osx-x86_64.jar netty-tcnative-2.0.20.Final-sources.jar netty-transport-4.1.32.Final-sources.jar netty-transport-4.1.32.Final.jar netty-transport-native-epoll-4.1.32.Final-linux-x86_64.jar netty-transport-native-epoll-4.1.32.Final-sources.jar netty-transport-native-kqueue-4.1.32.Final-osx-x86_64.jar netty-transport-native-kqueue-4.1.32.Final-sources.jar netty-transport-native-unix-common-4.1.32.Final-sources.jar netty-transport-native-unix-common-4.1.32.Final.jar netty-transport-rxtx-4.1.32.Final-sources.jar netty-transport-rxtx-4.1.32.Final.jar netty-transport-sctp-4.1.32.Final-sources.jar netty-transport-sctp-4.1.32.Final.jar netty-transport-udt-4.1.32.Final-sources.jar netty-transport-udt-4.1.32.Final.jar
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值