Netty接收发送UDP,使用反射进行解析和包装

对于发送的UDP数据包,要以Byte[]的形式包装为DatagramPacket后发送,接收时解析为Byte[],后解析出具体数据。

之前我的的文章有Netty接收和发送UDP的实现,这里不再赘述。

  • 先展示实体类。
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * @author qs
 * 2021.3.16
 * 数据包头实体类
 * --------------------------------------------------
 * 命名   意义    设计长度   Java类型
 * pver  协议版本  1Byte    byte
 * sid   信源标识  4Byte    int
 * did   信宿标识  4Byte    int
 * stime 发送时标  6Byte    short and int
 * dtype 数据类型标识  4Byte  int
 * dlength 数据体长度  2Byte  short
 * --------------------------------------------------
 */
@ToString
@Setter
@Getter
public class PackageHeader {
    public byte pver;
    public int sid;
    public int did;
    public short stime1;
    public int stime2;
    public int dtype;
    public short dlength;
}

  • 包装数据
// 合并两个Byte[]用的
ByteArrayOutputStream os = new ByteArrayOutputStream();
//这里转换,body转换原理一样
                byte[] header = Convertor.ObjectToByte(packageHeader);
//                byte[] body = Convertor.ObjectToByte(workingConInfo);
                byte[] body = Convertor.ObjectToByte(controlInstruInfo);
                os.write(header);
                os.write(body);
                byte[] data = os.toByteArray();
                udpClient.bind(6679, "127.0.0.1", 6678, "127.0.0.1")
                        .send(data);
  • 解析数据
import com.dpsm.yk.sbjk.Parser.Dao.ParseControlService;
import com.dpsm.yk.sbjk.Parser.Entity.ControlInstruInfo;
import com.dpsm.yk.sbjk.Parser.Entity.PackageHeader;
import com.dpsm.yk.sbjk.Tools.Util.Convertor;
import com.dpsm.yk.sbjk.Tools.Util.GlobalConstant;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;

import javax.servlet.ServletContext;
import java.util.Arrays;

/**
 * 对于新消息进行操作的类
 */
@Log4j2
@Component
public class NettyUDPServerHandler extends SimpleChannelInboundHandler<DatagramPacket> implements ServletContextAware {
    private ParseControlService parseControlService;
    private static ParseControlService parseControl;

    @Autowired
    public void setParseControlService(ParseControlService parseControlService) {
        this.parseControlService = parseControlService;
    }

    /**
     * 重写接收到的数据的具体操作
     *
     * @param channelHandlerContext
     * @param datagramPacket
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
//        parseControl.parseControl(datagramPacket.content());

        ByteBuf byteBuf = datagramPacket.content();
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        // 在这里转换为实体类
        byte[] header = Arrays.copyOfRange(bytes, 0, GlobalConstant.PACKAGE_HEADER_LENGTH);
        PackageHeader packageHeader = Convertor.byteToObject(header, PackageHeader.class);
        byte[] body = Arrays.copyOfRange(bytes, GlobalConstant.PACKAGE_HEADER_LENGTH, bytes.length);
        ControlInstruInfo controlInstruInfo = Convertor.byteToObject(body, ControlInstruInfo.class);
        System.out.println(packageHeader.toString());
        System.out.println(controlInstruInfo.toString());
    }

    /**
     * 出错回调
     *
     * @param channelHandlerContext
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        cause.printStackTrace();
        channelHandlerContext.close();
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        parseControl = this.parseControlService;
    }
}

  • 来看Convertor类实现,用反射实现的。
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 用来进行数据转换的类
 *
 * @author qs
 * 2021.3.16
 */
public class Convertor {
    /**
     * 字节数组向对象进行转化
     *
     * @param bytes 输入的byte数组
     * @param clazz 需要创建的对象类
     * @param <T>   泛型
     * @return 转换后的对象
     */
    public static <T> T byteToObject(byte[] bytes, Class<T> clazz) {
        T instance = null;
        try {
            instance = clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("实例化类失败", e);
        }

        List<Field> fields = new ArrayList<>();
        getFields(clazz, fields);
        ByteBuf buffer = Unpooled.buffer().writeBytes(bytes);
        for (Field field : fields) {
            fillData(field, instance, buffer);
        }

        return instance;
    }

    /**
     * 获取对象的变量
     *
     * @param clazz 类
     * @param list  变量存储列表
     * @return 变量列表
     */
    private static List<Field> getFields(Class clazz, List list) {
        Field[] fields = clazz.getDeclaredFields();
        list.addAll(Arrays.asList(fields));

        Class clazzSuper = clazz.getSuperclass();
        if (clazzSuper != null)
            getFields(clazzSuper, list);
        return list;
    }

    /**
     * 通过类型赋值
     *
     * @param field    属性
     * @param instance 对象
     * @param buffer   数据
     */
    private static void fillData(Field field, Object instance, ByteBuf buffer) {
        field.setAccessible(true);

        String typeName = field.getType().getName();
        try {
            switch (typeName) {
                case "java.lang.Boolean":
                case "boolean":
                    boolean b = buffer.readBoolean();
                    field.set(instance, b);
                    break;
                case "java.lang.Character":
                case "char":
                    CharSequence charSequence = buffer.readCharSequence(2, Charset.forName("UTF-8"));
                    field.set(instance, charSequence);
                    break;
                case "java.lang.Byte":
                case "byte":
                    byte b1 = buffer.readByte();
                    field.set(instance, b1);
                    break;
                case "java.lang.Short":
                case "short":
                    short readShort = buffer.readShort();
                    field.set(instance, readShort);
                    break;
                case "java.lang.Integer":
                case "int":
                    int readInt = buffer.readInt();
                    field.set(instance, readInt);
                    break;
                case "java.lang.Long":
                case "long":
                    long l = buffer.readLong();
                    field.set(instance, l);
                    break;
                case "java.lang.Float":
                case "float":
                    float readFloat = buffer.readFloat();
                    field.set(instance, readFloat);
                    break;
                case "java.lang.Double":
                case "double":
                    double readDouble = buffer.readDouble();
                    field.set(instance, readDouble);
                    break;
                case "java.util.Map":
                    Map<Byte, Byte> map = new HashMap<>();
                    while (true) {
                        try {
                            byte aByte = buffer.readByte();
                            byte aByte1 = buffer.readByte();
                            map.put(aByte, aByte1);
                        } catch (Exception e) {
                            field.set(instance, map);
                            break;
                        }
                    }
                    break;
                case "java.util.List":
                    List<Long> list = new ArrayList<>();
                    while (true) {
                        try {
                            long param = buffer.readLong();
                            list.add(param);
                        } catch (Exception e) {
                            field.set(instance, list);
                            break;
                        }
                    }
                    break;
                default:
                    Object o = field.getType().newInstance();
                    for (Field fieldSub : field.getType().getDeclaredFields()) {
                        fillData(fieldSub, o, buffer);
                    }
                    field.set(instance, o);
                    break;
            }
        } catch (Exception e) {
            throw new RuntimeException(typeName + "读取失败,field:" + field.getName(), e);
        }
    }

    /**
     * 对象转ByteBuf
     *
     * @param o   要转换的对象
     * @param <T> 类型
     * @return 转换结果
     */
    public static <T> byte[] ObjectToByte(T o) {
        List<Field> fields = new ArrayList<>();
        getFields(o.getClass(), fields);
        ByteBuf byteBuf = Unpooled.buffer();
        for (Field field : fields) {
            write2ByteBuf(field, o, byteBuf);
        }
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        return bytes;
    }

    /**
     * 为ByteBuf赋值
     *
     * @param field    属性
     * @param instance 对象
     * @param buffer   buffer
     */
    private static void write2ByteBuf(Field field, Object instance, ByteBuf buffer) {
        field.setAccessible(true);

        String typeName = field.getType().getName();

        Object value = null;
        try {
            value = field.get(instance);
        } catch (IllegalAccessException e) {
            new RuntimeException("反射获取值失败,filed:" + field.getName(), e);
        }
        if (value == null)
            return;
        switch (typeName) {
            case "java.lang.Boolean":
            case "boolean":
                buffer.writeBoolean((Boolean) value);
                break;
            case "java.lang.Character":
            case "char":
                buffer.writeCharSequence((CharSequence) value, Charset.forName("UTF-8"));
                break;
            case "java.lang.Byte":
            case "byte":
                buffer.writeByte((byte) value);
                break;
            case "java.lang.Short":
            case "short":
                buffer.writeShort((short) value);
                break;
            case "java.lang.Integer":
            case "int":
                buffer.writeInt((int) value);
                break;
            case "java.lang.Long":
            case "long":
                buffer.writeLong((long) value);
                break;
            case "java.lang.Float":
            case "float":
                buffer.writeFloat((float) value);
                break;
            case "java.lang.Double":
            case "double":
                buffer.writeDouble((double) value);
                break;
            case "java.lang.String":
                buffer.writeCharSequence((CharSequence) value, Charset.forName("UTF-8"));
                break;
            case "java.time.LocalDateTime":
                String s = ((LocalDateTime) value).toString();
                buffer.writeCharSequence((CharSequence) s, Charset.forName("UTF-8"));
                break;
            case "java.util.Map":
                try {
                    Map<Byte, Byte> map = (Map<Byte, Byte>) value;
                    for (byte key : map.keySet()) {
                        buffer.writeByte(key);
                        buffer.writeByte(map.get(key));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            case "java.util.List":
                try {
                    List<Long> list = (List<Long>) value;
                    for (long param : list) {
                        buffer.writeLong(param);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            default:
                for (Field fieldSub : field.getType().getDeclaredFields()) {
                    write2ByteBuf(fieldSub, value, buffer);
                }
                break;
        }
    }
}
  • 输出

PackageHeader(pver=1, sid=1024, did=1007, stime1=2, stime2=222, dtype=777, dlength=22)

  • 完事.
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Netty 接收发送十六进制数据需要进行编解码器的配置。 首先,创建一个 `ByteToMessageCodec` 类型的编解码器,这个编解码器可以将字节数据转换成对象,或者将对象转换成字节数据。下面是一个将字节数据解码成十六进制字符串的示例代码: ```java public class HexDecoder extends ByteToMessageCodec<String> { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() < 2) { return; } // 读取字节数据并转换成十六进制字符串 byte[] bytes = new byte[in.readableBytes()]; in.readBytes(bytes); String hex = Hex.encodeHexString(bytes); out.add(hex); } } ``` 接下来,创建一个 `MessageToByteEncoder` 类型的编码器,这个编码器可以将对象转换成字节数据。下面是一个将十六进制字符串编码成字节数据的示例代码: ```java public class HexEncoder extends MessageToByteEncoder<String> { @Override protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { // 将十六进制字符串转换成字节数据 byte[] bytes = Hex.decodeHex(msg.toCharArray()); out.writeBytes(bytes); } } ``` 最后,在 Netty 的管道中添加编解码器即可: ```java ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HexDecoder()); pipeline.addLast(new HexEncoder()); ``` 这样就完成了对十六进制数据的接收发送。在使用时,只需要将十六进制字符串作为消息对象传递给 Netty 的 `Channel` 即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值