Build your own IoT platform from scratch (4) Realize dynamic protocol analysis based on protocol packages

source code

source code repository

Past links

Build your own IoT platform from scratch (1) Requirements analysis and architecture design

Build your own IoT platform from scratch (2) Realize a subscription-based message bus

Build your own IoT platform from scratch (3) ) Implement mqtt server gateway based on netty

Business needs

In the IoT platform scenario, there are various access devices. At this time, it cannot be implemented in the source code in the form of hard coding, so it is necessary to implement a dynamic protocol analysis method that can be freely added and modified.

Method to realize

For this scenario, it is realized by loading the jar package, specifying the standard interface, as long as the corresponding interface is implemented in the jar package, and the standard protocol analysis method is enough (note here, it is necessary to support the analysis of the uplink and downlink messages of the device ).

Source code implementation

protocol object interface

/**
 * 消息协议支持接口,通过实现此接口来自定义消息协议
 *
 * @author liuhualin
 * @since 1.0.0
 */
public interface ProtocolSupport extends Serializable {
    
    
    /**
     * @return 协议ID
     */
    String getId();

    /**
     * @return 协议名称
     */
    String getName();

    /**
     * @return 说明
     */
    String getDescription();

    /**
     * @return 获取支持的协议类型
     */
    List<Transport> getSupportedTransport();

    /**
     * 根据指定协议取得
     *
     * @return 获取支持的协议类型
     */
    DeviceMessageCodec getMessageCodecSupport(String transport);
}

The implementation class of the protocol object

Define the functions that the protocol parsing package object should have

  • Uplink protocol analysis
  • Downlink protocol analysis

DeviceMessageCodec here inherits two interfaces, which are used to implement uplink parsing and downlink parsing respectively, and DeviceMessageCodec defines the protocol type corresponding to this parser. The final structure is as follows: a protocol package may have multiple DeviceMessageCodecs (according to the protocol
package The parsed network protocol comes from, for example, two DeviceMessageCodecs need to be parsed if TCP and MQTT are needed), and each DeviceMessageCodec realizes two functions of uplink message parsing and downlink message parsing.

/**
 * 实现自定义消息解析需要实现该类
 */
public interface ProtocolSupportProvider {
    
    

    ProtocolSupport create();
}

/**
 * 多传输协议支持的解析包
 */
@Data
public class CompositeProtocolSupport implements ProtocolSupport {
    
    

    /**
     * 协议ID
     */
    private String id;

    /**
     * 协议名称
     */
    private String name;

    /**
     * 协议描述
     */
    private String description;

    /**
     * 支持的协议类型
     */
    private Map<String, DeviceMessageCodec> messageCodecSupports = new ConcurrentHashMap<>();


    @Override
    public String getId() {
    
    
        return this.id;
    }

    @Override
    public String getName() {
    
    
        return this.name;
    }

    @Override
    public String getDescription() {
    
    
        return this.description;
    }

    @Override
    public List<Transport> getSupportedTransport() {
    
    
        return messageCodecSupports.entrySet().stream().map(Map.Entry::getValue).map(DeviceMessageCodec::getSupportTransport).collect(Collectors.toList());
    }

    @Override
    public DeviceMessageCodec getMessageCodecSupport(String transport) {
    
    
        return messageCodecSupports.get(transport);
    }

    public void addMessageCodecSupport(Transport transport, DeviceMessageCodec codec) {
    
    
        messageCodecSupports.put(transport.getId(), codec);
    }

    public void addMessageCodecSupport(DeviceMessageCodec codec) {
    
    
        addMessageCodecSupport(codec.getSupportTransport(), codec);
    }
}

Parsing protocol packets

Parse the jar package in the form of classloader, and call the create of ProtocolSupportProvider reflectively

**
 * 自定义协议支持包注册器(内存)
 */
@Component
@Slf4j
public class MemoryProtocolSupportRegistry {
    
    

    /**
     * 协议支持包
     * <p>
     * K: 协议ID
     * V: 协议实体
     */
    private Map<String, ProtocolSupport> protocolSupportMap = new ConcurrentHashMap<>(16);

    /**
     * 取得解析器
     *
     * @param protocolId 协议ID
     */
    public ProtocolSupport getProtocolSupport(String protocolId) {
    
    
        return protocolSupportMap.get(protocolId);
    }

    /**
     * 发布协议
     */
    public void publishProtocol(String jarFilePath, String mainClass, String protocolId) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    
    
        decodeProtocolJar(jarFilePath,  mainClass,  protocolId);
    }

    /**
     * 发布协议
     */
    public void publishProtocol(ProtocolSupport protocolSupport) {
    
    
        protocolSupportMap.put(protocolSupport.getId(), protocolSupport);
    }

    /**
     * 解析协议jar包
     *
     * @param jarFilePath
     * @throws IllegalAccessException
     */
    private void decodeProtocolJar(String jarFilePath, String mainClass, String protocolId) {
    
    

        File jarFile = new File(jarFilePath);
        URL url;
        try {
    
    
            url = jarFile.toURI().toURL();
        } catch (Exception e) {
    
    
            throw new ServiceException("URL错误,加载不到jar");
        }
        if (null != url) {
    
    
            try {
    
    
                URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{
    
    url}, this.getClass().getClassLoader());
                Class<?> codec = urlClassLoader.loadClass(mainClass);
                Object instance = codec.getClass().forName(mainClass, true, urlClassLoader).newInstance();
                Method method = codec.getMethod("create");
                // 加载协议包,创建实体对象
                ProtocolSupport protocolSupport = (ProtocolSupport) method.invoke(instance);
                if (!StringUtils.equals(protocolSupport.getId(), protocolId)) {
    
    
                    throw new ServiceException("jar包内定义的ID与协议ID不匹配");
                }
                protocolSupportMap.put(protocolSupport.getId(), protocolSupport);
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
    
    
                throw new ServiceException("jar包解析异常");
            }
        }
    }


}

Guess you like

Origin blog.csdn.net/baidu_29609961/article/details/131898956