# 协议开发说明 平台封装了网络通信,但是具体的数据由消息协议进行解析.`协议(ProtocolSupport)`主要由`认证器(Authenticator)`, `消息编解码器(DeviceMessageCodec)`,`消息发送拦截器(DeviceMessageSenderInterceptor)`以及`配置元数据(ConfigMetadata)`组成. ## 认证器 认证器(Authenticator)是用于在收到设备请求(例如MQTT)时,对客户端进行认证时使用,不同的网络协议(Transport)使用不同的认证器. 接口定义: ```java public interface Authenticator { /** * 对指定对设备进行认证 * * @param request 认证请求 * @param device 设备 * @return 认证结果 */ Mono authenticate(@Nonnull AuthenticationRequest request, @Nonnull DeviceOperator device); /** * 在MQTT服务网关中指定了认证协议时,将调用此方法进行认证。 * 注意: 认证通过后,需要设置设备ID.{@link AuthenticationResponse#success(String)} * @param request 认证请求 * @param registry 设备注册中心 * @return 认证结果 */ default Mono authenticate(@Nonnull AuthenticationRequest request, @Nonnull DeviceRegistry registry) { return Mono.just(AuthenticationResponse.success()); } } ``` 参数`AuthenticationRequest`为认证请求参数,不同的网络类型请求类型也不同,请根据实际情况转换为对应的类型,例如: `MqttAuthenticationRequest mqttRequest = (MqttAuthenticationRequest)request;` 参数`DeviceOperator`为对应的设备操作接口,可通过此接口获取设备的配置,例如:`device.getConfig("mqttUsername")`. 返回值`Mono`为认证结果. 例: ```java Authenticator mqttAuthenticator = (request, device) -> { MqttAuthenticationRequest mqttRequest = ((MqttAuthenticationRequest) request); return device.getConfigs("username", "password") //获取设备的配置信息,由配置元数据定义,在设备型号中进行配置. .flatMap(values -> { String username = values.getValue("username").map(Value::asString).orElse(null); String password = values.getValue("password").map(Value::asString).orElse(null); if (mqttRequest.getUsername().equals(username) && mqttRequest.getPassword().equals(password)) { return Mono.just(AuthenticationResponse.success()); } else { return Mono.just(AuthenticationResponse.error(400, "密码错误")); } }); } ``` ## 消息编解码器 用于将平台`统一的消息(Message)`与`设备端能处理的消息(EncodedMessage)`进行相互转换. 设备网关从`网络组件`中接收到报文后,会调用对应协议包的消息编解码器进行处理. 接口(`DeviceMessageCodec`)定义: ```java class DeviceMessageCodec{ //此编解码器支持的网络协议,如: DefaultTransport.MQTT Transport getSupportTransport(); //将平台发往设备的消息编码为设备端对消息 Publisher encode(MessageEncodeContext context); //将设备发往平台的消息解码为平台统一的消息 Publisher decode(MessageDecodeContext context); } ``` ::: tip 注意 方法返回值是响应式结果,根据情况返回`Mono`(单条消息)或者`Flux`(多条消息). ::: ## 上下文 ### 编码上下文类结构 ```java class MessageEncodeContext{ //获取当前设备操作接口,可通过此接口获取对应设备的配置等信息 DeviceOperator getDevice(); //平台下发的指令,具体请查看平台统一设备消息定义 Message getMessage(); //强制回复设备消息,在http等场景下,通过调用http api下发指令,然后直接调用此方法回复结果即可. Mono reply(Publisher replyMessage); //获取当前会话,需要将MessageEncodeContext强制转换为ToDeviceMessageContext DeviceSession getSession(); } ``` ### 解码上下文类结构 ```java class class MessageDecodeContext{ //获取当前设备操作接口,可通过此接口获取对应设备的配置等信息 DeviceOperator getDevice(); //从网络组件中接收到的消息,不同的网络组件消息类型不同, //使用时根据网络方式强制转换为对应的类型. EncodedMessage getMessage(); } ``` ::: tip 注意 不同的网络协议需要转换为不同的`EncodedMessage`类型.比如,MQTT需要转换为`MqttMessage`. 大部分情况下:`MessageDecodeContext`可转为`FromDeviceMessageContext`,可获取到当前设备的连接会话`DeviceSession`,通过会话可以直接发送消息到设备. ::: ### EncodedMessage 从网络组件中接收到的消息,不同的网络组件消息类型不同。 公共方法: ```java class EncodedMessage{ //获取原始报文 ByteBuf getPayload(); //报文转为字符串 String payloadAsString(); //报文转为JSON对象 JSONObject payloadAsJson(); //报文转为JSON数组 JSONArray payloadAsJsonArray(); // 报文转为字节数组 byte[] payloadAsBytes() } ``` ### MQTT消息 ```java class MqttMessage extends EncodedMessage{ String getTopic(); int getQos(); } ``` ### HTTP消息 如果是`POST`,`PUT`,`PATCH`等请求,`EncodedMessage.getPayload`即为请求体. ```java class HttpExchangeMessage{ String getUrl(); String getPath(); HttpMethod getMethod(); MediaType getContentType(); //请求头 List
getHeaders(); //url上的查询参数 Map getQueryParameters(); //POST application/x-www-form-urlencoded时的请求参数 Map getRequestParam(); //响应成功 Mono ok(String msg); //响应失败 Mono error(int status,String msg); } ``` ### CoAP消息 ```java CoapExchangeMessage{ String getPath(); CoAP.Code getCode(); List