本篇从MQTT
入手,研究相关源码
通过之前对代码结构的浏览,我们了解到MQTT
的实现位于common/transport/mqtt模块
接下来以org.thingsboard.server.transport.mqtt.MqttTransportService
为入口开始阅读
MqttTransportService
@Service("MqttTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
@Service
实例化当前类并注册为MqttTransportService
@ConditionalOnExpression
定义了启用条件
@PostConstruct
public void init() throws Exception {
log.info("Setting resource leak detector level to {}", leakDetectorLevel);
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
log.info("Starting MQTT transport...");
bossGroup = new NioEventLoopGroup(bossGroupThreadCount);
workerGroup = new NioEventLoopGroup(workerGroupThreadCount);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MqttTransportServerInitializer(context, false))
.childOption(ChannelOption.SO_KEEPALIVE, keepAlive);
serverChannel = b.bind(host, port).sync().channel();
if (sslEnabled) {
b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MqttTransportServerInitializer(context, true))
.childOption(ChannelOption.SO_KEEPALIVE, keepAlive);
sslServerChannel = b.bind(sslHost, sslPort).sync().channel();
}
log.info("Mqtt transport started!");
}
熟悉Netty
的话从即可看出MQTT
服务正是基于Netty
实现的
.childHandler(new MqttTransportServerInitializer(context, false))
可知处理器为org.thingsboard.server.transport.mqtt.MqttTransportServerInitializer
构造参数context为传输上下文org.thingsboard.server.transport.mqtt.MqttTransportContext
@Autowired
private MqttTransportContext context;
MqttTransportContext
@Component
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
@Getter
@Autowired(required = false)
private MqttSslHandlerProvider sslHandlerProvider;
SSL
处理器提供者
@Getter
@Autowired
private JsonMqttAdaptor jsonMqttAdaptor;
JSON
格式的适配器org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor
@Getter
@Autowired
private ProtoMqttAdaptor protoMqttAdaptor;
Proto
格式的适配器org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor
接着阅读处理器代码
MqttTransportServerInitializer
继承自io.netty.channel.ChannelInitializer
//org.thingsboard.server.transport.mqtt.MqttTransportServerInitializer
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
SslHandler sslHandler = null;
if (context.isProxyEnabled()) {
pipeline.addLast("proxy", new HAProxyMessageDecoder());
pipeline.addLast("ipFilter", new ProxyIpFilter(context));
} else {
pipeline.addLast("ipFilter", new IpFilter(context));
}
if (sslEnabled && context.getSslHandlerProvider() != null) {
sslHandler = context.getSslHandlerProvider().getSslHandler();
pipeline.addLast(sslHandler);
}
pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize()));
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
MqttTransportHandler handler = new MqttTransportHandler(context, sslHandler);
pipeline.addLast(handler);
ch.closeFuture().addListener(handler);
}
pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize()));
解码器,解析数据包,为实现MQTT
协议的关键,由Netty
提供
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
编码器,构建数据包,为实现MQTT
协议的关键,由Netty
提供
MqttTransportHandler handler = new MqttTransportHandler(context, sslHandler);
pipeline.addLast(handler);
消息处理器org.thingsboard.server.transport.mqtt.MqttTransportHandler
此为MQTT
消息处理的核心类
MqttTransportHandler
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.trace("[{}] Processing msg: {}", sessionId, msg);
if (address == null) {
address = getAddress(ctx);
}
try {
if (msg instanceof MqttMessage) {
MqttMessage message = (MqttMessage) msg;
if (message.decoderResult().isSuccess()) {
processMqttMsg(ctx, message);
} else {
log.error("[{}] Message decoding failed: {}", sessionId, message.decoderResult().cause().getMessage());
ctx.close();
}
} else {
log.debug("[{}] Received non mqtt message: {}", sessionId, msg.getClass().getSimpleName());
ctx.close();
}
} finally {
ReferenceCountUtil.safeRelease(msg);
}
}
处理消息
processMqttMsg(ctx, message);
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) {
if (msg.fixedHeader() == null) {
log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort());
ctx.close();
return;
}
deviceSessionCtx.setChannel(ctx);
if (CONNECT.equals(msg.fixedHeader().messageType())) {
processConnect(ctx, (MqttConnectMessage) msg);
} else if (deviceSessionCtx.isProvisionOnly()) {
processProvisionSessionMsg(ctx, msg);
} else {
enqueueRegularSessionMsg(ctx, msg);
}
}
processConnect(ctx, (MqttConnectMessage) msg);
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
log.debug("[{}][{}] Processing connect msg for client: {}!", address, sessionId, msg.payload().clientIdentifier());
String userName = msg.payload().userName();
String clientId = msg.payload().clientIdentifier();
if (DataConstants.PROVISION.equals(userName) || DataConstants.PROVISION.equals(clientId)) {
deviceSessionCtx.setProvisionOnly(true);
ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED, msg));
} else {
X509Certificate cert;
if (sslHandler != null && (cert = getX509Certificate()) != null) {
processX509CertConnect(ctx, cert, msg);
} else {
processAuthTokenConnect(ctx, msg);
}
}
}
由代码可知处理逻辑。若为预配置(用户名或客户端id为provision),则标记设备会话上下文并返回响应消息;否则根据是否启用SSL
执行对应的处理
我们以Token
验证方式继续探究
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage connectMessage) {
String userName = connectMessage.payload().userName();
log.debug("[{}][{}] Processing connect msg for client with user name: {}!", address, sessionId, userName);
TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
.setClientId(connectMessage.payload().clientIdentifier());
if (userName != null) {
request.setUserName(userName);
}
byte[] passwordBytes = connectMessage.payload().passwordInBytes();
if (passwordBytes != null) {
String password = new String(passwordBytes, CharsetUtil.UTF_8);
request.setPassword(password);
}
transportService.process(DeviceTransportType.MQTT, request.build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
onValidateDeviceResponse(msg, ctx, connectMessage);
}
@Override
public void onError(Throwable e) {
log.trace("[{}] Failed to process credentials: {}", address, userName, e);
ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage));
ctx.close();
}
});
}
这里提一下如下代码
TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
.setClientId(connectMessage.payload().clientIdentifier());
此处TransportProtos
由Protobuf
定义,TransportProtos.ValidateBasicMqttCredRequestMsg.Builder
和TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
方法均为自动生成
syntax = "proto3";
package transport;
option java_package = "org.thingsboard.server.gen.transport";
option java_outer_classname = "TransportProtos";
message ValidateBasicMqttCredRequestMsg {
string clientId = 1;
string userName = 2;
string password = 3;
}
先使用newBuilder()
方法创建构造器,然后调用相关属性的set
方法赋值,最后通过build()
方法构造实例
接着调用transportService.process
方法处理鉴权请求
DefaultTransportService
//org.thingsboard.server.common.transport.service.DefaultTransportService
@Override
public void process(DeviceTransportType transportType, TransportProtos.ValidateBasicMqttCredRequestMsg msg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
log.trace("Processing msg: {}", msg);
TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(),
TransportApiRequestMsg.newBuilder().setValidateBasicMqttCredRequestMsg(msg).build());
doProcess(transportType, protoMsg, callback);
}
封装验证请求,调用doProcess
方法
//org.thingsboard.server.common.transport.service.DefaultTransportService
private void doProcess(DeviceTransportType transportType, TbProtoQueueMsg<TransportApiRequestMsg> protoMsg,
TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
ListenableFuture<ValidateDeviceCredentialsResponse> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> {
TransportProtos.ValidateDeviceCredentialsResponseMsg msg = tmp.getValue().getValidateCredResponseMsg();
ValidateDeviceCredentialsResponse.ValidateDeviceCredentialsResponseBuilder result = ValidateDeviceCredentialsResponse.builder();
if (msg.hasDeviceInfo()) {
result.credentials(msg.getCredentialsBody());
TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo());
result.deviceInfo(tdi);
ByteString profileBody = msg.getProfileBody();
if (!profileBody.isEmpty()) {
DeviceProfile profile = deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody);
if (transportType != DeviceTransportType.DEFAULT
&& profile != null && profile.getTransportType() != DeviceTransportType.DEFAULT && profile.getTransportType() != transportType) {
log.debug("[{}] Device profile [{}] has different transport type: {}, expected: {}", tdi.getDeviceId(), tdi.getDeviceProfileId(), profile.getTransportType(), transportType);
throw new IllegalStateException("Device profile has different transport type: " + profile.getTransportType() + ". Expected: " + transportType);
}
result.deviceProfile(profile);
}
}
return result.build();
}, MoreExecutors.directExecutor());
AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor);
}
方法内容看起来很多,其实也就两步
首先通过Futures.transform
方法根据transportApiRequestTemplate.send(protoMsg)
的返回结果,转换为org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse
,写入设备信息
接着设置回调方法
AsyncCallbackTemplate
//org.thingsboard.server.queue.common.AsyncCallbackTemplate
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
Consumer<Throwable> onFailure, Executor executor) {
FutureCallback<T> callback = new FutureCallback<T>() {
@Override
public void onSuccess(T result) {
try {
onSuccess.accept(result);
} catch (Throwable th) {
onFailure(th);
}
}
@Override
public void onFailure(Throwable t) {
onFailure.accept(t);
}
};
if (executor != null) {
Futures.addCallback(future, callback, executor);
} else {
Futures.addCallback(future, callback, MoreExecutors.directExecutor());
}
}
使用Futures.addCallback
方法设置回调方法
现在,我们回头看成功和失败的回调方法,它位于MqttTransportHandler
的processAuthTokenConnect
方法中
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
onValidateDeviceResponse(msg, ctx, connectMessage);
}
成功则调用onValidateDeviceResponse
方法
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
private void onValidateDeviceResponse(ValidateDeviceCredentialsResponse msg, ChannelHandlerContext ctx, MqttConnectMessage connectMessage) {
if (!msg.hasDeviceInfo()) {
context.onAuthFailure(address);
ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED, connectMessage));
ctx.close();
} else {
context.onAuthSuccess(address);
deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo());
deviceSessionCtx.setDeviceProfile(msg.getDeviceProfile());
deviceSessionCtx.setSessionInfo(SessionInfoCreator.create(msg, context, sessionId));
transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_OPEN, new TransportServiceCallback<Void>() {
@Override
public void onSuccess(Void msg) {
SessionMetaData sessionMetaData = transportService.registerAsyncSession(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.this);
checkGatewaySession(sessionMetaData);
ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED, connectMessage));
deviceSessionCtx.setConnected(true);
log.debug("[{}] Client connected!", sessionId);
transportService.getCallbackExecutor().execute(() -> processMsgQueue(ctx)); //this callback will execute in Producer worker thread and hard or blocking work have to be submitted to the separate thread.
}
@Override
public void onError(Throwable e) {
if (e instanceof TbRateLimitsException) {
log.trace("[{}] Failed to submit session event: {}", sessionId, e.getMessage());
} else {
log.warn("[{}] Failed to submit session event", sessionId, e);
}
ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage));
ctx.close();
}
});
}
}
若响应消息中不包含设备信息,则认证失败,直接返回认证失败
先向设备会话上下文中写入相关信息,之后调用transportService.process
处理会话开启事件
DefaultTransportService
//org.thingsboard.server.common.transport.service.DefaultTransportService
@Override
public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, msg, callback)) {
reportActivityInternal(sessionInfo);
sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
.setSessionEvent(msg).build(), callback);
}
}
checkLimits
方法用于检查传输的速率限制,有兴趣自行了解
reportActivityInternal
方法用户更新活动时间,有兴趣自行了解
调用sendToDeviceActor
方法发送会话消息
//org.thingsboard.server.common.transport.service.DefaultTransportService
protected void sendToDeviceActor(TransportProtos.SessionInfoProto sessionInfo, TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback<Void> callback) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, getTenantId(sessionInfo), getDeviceId(sessionInfo));
if (log.isTraceEnabled()) {
log.trace("[{}][{}] Pushing to topic {} message {}", getTenantId(sessionInfo), getDeviceId(sessionInfo), tpi.getFullTopicName(), toDeviceActorMsg);
}
TransportTbQueueCallback transportTbQueueCallback = callback != null ?
new TransportTbQueueCallback(callback) : null;
tbCoreProducerStats.incrementTotal();
StatsCallback wrappedCallback = new StatsCallback(transportTbQueueCallback, tbCoreProducerStats);
tbCoreMsgProducer.send(tpi,
new TbProtoQueueMsg<>(getRoutingKey(sessionInfo),
ToCoreMsg.newBuilder().setToDeviceActorMsg(toDeviceActorMsg).build()),
wrappedCallback);
}
封装回调对象,向核心模块发送消息
最后,回看一下回调处理,位于MqttTransportHandler
的onValidateDeviceResponse
的方法中
@Override
public void onSuccess(Void msg) {
SessionMetaData sessionMetaData = transportService.registerAsyncSession(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.this);
checkGatewaySession(sessionMetaData);
ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED, connectMessage));
deviceSessionCtx.setConnected(true);
log.debug("[{}] Client connected!", sessionId);
transportService.getCallbackExecutor().execute(() -> processMsgQueue(ctx)); //this callback will execute in Producer worker thread and hard or blocking work have to be submitted to the separate thread.
}
registerAsyncSession
用于记录会话,有兴趣自行了解
checkGatewaySession
用于检查是否为网关会话(重写活动时间),有兴趣自行了解
接着返回连接成功的消息
由于认证处理是异步的,故将收到的数据包先保存在队列中,认证成功后调用processMsgQueue
方法处理队列中的消息
关于processMsgQueue
方法后面再看
@Override
public void onError(Throwable e) {
if (e instanceof TbRateLimitsException) {
log.trace("[{}] Failed to submit session event: {}", sessionId, e.getMessage());
} else {
log.warn("[{}] Failed to submit session event", sessionId, e);
}
ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage));
ctx.close();
}
返回服务不可用
@Override
public void onError(Throwable e) {
log.trace("[{}] Failed to process credentials: {}", address, userName, e);
ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage));
ctx.close();
}
返回服务不可用
至此,连接消息的处理逻辑已基本清楚
processProvisionSessionMsg(ctx, msg);
用于使用指定的设备配置创建设备
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
private void processProvisionSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) {
switch (msg.fixedHeader().messageType()) {
case PUBLISH:
MqttPublishMessage mqttMsg = (MqttPublishMessage) msg;
String topicName = mqttMsg.variableHeader().topicName();
int msgId = mqttMsg.variableHeader().packetId();
try {
if (topicName.equals(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC)) {
try {
TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getJsonMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg);
transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg));
log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId);
} catch (Exception e) {
if (e instanceof JsonParseException || (e.getCause() != null && e.getCause() instanceof JsonParseException)) {
TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getProtoMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg);
transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg));
deviceSessionCtx.setProvisionPayloadType(TransportPayloadType.PROTOBUF);
log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId);
} else {
throw e;
}
}
} else {
log.debug("[{}] Unsupported topic for provisioning requests: {}!", sessionId, topicName);
ctx.close();
}
} catch (RuntimeException e) {
log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
ctx.close();
} catch (AdaptorException e) {
log.debug("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
ctx.close();
}
break;
case PINGREQ:
ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0)));
break;
case DISCONNECT:
ctx.close();
break;
}
}
仅处理发布至 /provision/request 主题的消息,先尝试以JSON
格式化数据,若数据不是JSON
格式,则以ProtoBuf
格式化数据
调用process
方法处理请求,有兴趣自行了解
enqueueRegularSessionMsg(ctx, msg);
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
void enqueueRegularSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) {
final int queueSize = deviceSessionCtx.getMsgQueueSize();
if (queueSize >= context.getMessageQueueSizePerDeviceLimit()) {
log.info("Closing current session because msq queue size for device {} exceed limit {} with msgQueueSize counter {} and actual queue size {}",
deviceSessionCtx.getDeviceId(), context.getMessageQueueSizePerDeviceLimit(), queueSize, deviceSessionCtx.getMsgQueueSize());
ctx.close();
return;
}
deviceSessionCtx.addToQueue(msg);
processMsgQueue(ctx); //Under the normal conditions the msg queue will contain 0 messages. Many messages will be processed on device connect event in separate thread pool
}
检查队列长度是未超限后,将消息添加至队列,然后调用processMsgQueue
方法进行处理
//org.thingsboard.server.common.transport.service.DefaultTransportService
void processMsgQueue(ChannelHandlerContext ctx) {
if (!deviceSessionCtx.isConnected()) {
log.trace("[{}][{}] Postpone processing msg due to device is not connected. Msg queue size is {}", sessionId, deviceSessionCtx.getDeviceId(), deviceSessionCtx.getMsgQueueSize());
return;
}
deviceSessionCtx.tryProcessQueuedMsgs(msg -> processRegularSessionMsg(ctx, msg));
}
判断设备连接后调用org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx
的tryProcessQueuedMsgs
方法处理队列消息
DeviceSessionCtx
//org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx
public void tryProcessQueuedMsgs(Consumer<MqttMessage> msgProcessor) {
while (!msgQueue.isEmpty()) {
if (msgQueueProcessorLock.tryLock()) {
try {
MqttMessage msg;
while ((msg = msgQueue.poll()) != null) {
try {
msgQueueSize.decrementAndGet();
msgProcessor.accept(msg);
} finally {
ReferenceCountUtil.safeRelease(msg);
}
}
} finally {
msgQueueProcessorLock.unlock();
}
} else {
return;
}
}
}
加锁保证了消息的顺序执行
接着看msgProcessor.accept(msg)
,即上文msg -> processRegularSessionMsg(ctx, msg)
//org.thingsboard.server.transport.mqtt.MqttTransportHandler
void processRegularSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) {
switch (msg.fixedHeader().messageType()) {
case PUBLISH:
processPublish(ctx, (MqttPublishMessage) msg);
break;
case SUBSCRIBE:
processSubscribe(ctx, (MqttSubscribeMessage) msg);
break;
case UNSUBSCRIBE:
processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
break;
case PINGREQ:
if (checkConnected(ctx, msg)) {
ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0)));
transportService.reportActivity(deviceSessionCtx.getSessionInfo());
}
break;
case DISCONNECT:
ctx.close();
break;
case PUBACK:
int msgId = ((MqttPubAckMessage) msg).variableHeader().messageId();
TransportProtos.ToDeviceRpcRequestMsg rpcRequest = rpcAwaitingAck.remove(msgId);
if (rpcRequest != null) {
transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY);
}
break;
default:
break;
}
}
根据消息类型进行后续的处理