相对于服务端,客户端稍微简单一些,因为它不需要关心Retain(是否需要缓存消息)、连接、管理订阅者等这些费时费力的事情,服务端的话可以参考上一篇文章:springboot+netty+mqtt服务端实现
/**
* @author: zhouwenjie
* @description: 客户端
* @create: 2020-04-03 17:14
**/
@Slf4j
@Component
public class MqttClient {
@Value("${driver.mqtt.server_host}")
private String hostServer;
@Value("${driver.mqtt.server_port}")
private int portServer;
@Autowired
private ClientMqttHandler clientMqttHandler;
private NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
private Bootstrap bootstrap;
public void run() {
bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
//客户端初始化
socketChannel.pipeline().addLast("decoder", new MqttDecoder(1024 * 8));
socketChannel.pipeline().addLast("encoder", MqttEncoder.INSTANCE);
socketChannel.pipeline().addLast(clientMqttHandler);
}
});
//连接netty服务器
reconnect(hostServer, portServer);
}
/**
* 功能描述: 断线重连,客户端有断线重连机制,就更不能使用异步阻塞了
*
* @param
* @return void
* @author zhouwenjie
* @date 2021/3/19 14:53
*/
public void reconnect(String host, Integer port) {
bootstrap.remoteAddress(host, port);
ChannelFuture channelFuture = bootstrap.connect();
//使用最新的ChannelFuture -> 开启最新的监听器
channelFuture.addListener((ChannelFutureListener) future -> {
if (future.cause() != null) {
log.error("MQTT驱动服务端" + host + ":" + port + "连接失败。。。");
future.channel().eventLoop().schedule(() -> reconnect(host, port), 3, TimeUnit.SECONDS);
} else {
log.info("MQTT驱动服务端" + host + ":" + port + "连接成功");
}
});
}
/**
* 关闭 client
*/
@PreDestroy
public void shutdown() {
// 优雅关闭 EventLoopGroup 对象
eventLoopGroup.shutdownGracefully();
log.info("[*MQTT客户端关闭]");
}
}
/**
* @author: zhouwenjie
* @description: 客户端处理类
* @create: 2020-04-03 17:45
*
**/
@Component
@Slf4j
@ChannelHandler.Sharable
public class ClientMqttHandler extends SimpleChannelInboundHandler<MqttMessage> {
/**
* 注入NettyClient
*/
@Autowired
private MqttClient mqttClient;
@Value("${driver.mqtt.server_host}")
private String hostServer;
@Value("${driver.mqtt.server_port}")
private int portServer;
public static ChannelHandlerContext context;
@Autowired
private MqttMsgBack mqttMsgBack;
/**
* 连接成功
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
int port = ipSocket.getPort();
String host = ipSocket.getHostString();
if (hostServer.equals(host) && port == portServer) {
context = ctx;
mqttMsgBack.connect(ctx);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
int port = ipSocket.getPort();
String host = ipSocket.getHostString();
log.error("与设备" + host + ":" + port + "连接断开!" + "断开定时发送");
ctx.close();
ctx.deregister();
ctx.pipeline().remove(this);
super.channelInactive(ctx);
mqttClient.reconnect(ipSocket.getHostString(), ipSocket.getPort());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
log.error("[* Netty connection exception]:{}", cause.toString());
cause.printStackTrace();
}
@Override
public void channelRead0(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
if (null != mqttMessage) {
log.info("接收mqtt消息:" + mqttMessage);
MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader();
switch (mqttFixedHeader.messageType()) {
// ----------------------发送消息端(客户端)可能会触发的事件----------------------------------------------------------------
case CONNACK:
mqttMsgBack.receiveConnectionAck(ctx, mqttMessage);
break;
case PUBREC:
case PUBACK:
//接收服务端的ack消息
mqttMsgBack.receivePubAck(ctx, mqttMessage);
break;
case PUBCOMP:
mqttMsgBack.receivePubcomp(ctx, mqttMessage);
break;
case SUBACK:
mqttMsgBack.receiveSubAck(ctx, mqttMessage);
break;
case UNSUBACK:
mqttMsgBack.receiveUnSubAck(ctx, mqttMessage);
break;
case PINGRESP:
//客户端发起心跳
mqttMsgBack.pingReq(ctx, mqttMessage);
break;
// ----------------------接收消息端(客户端)可能会触发的事件----------------------------------------------------------------
case PUBLISH:
// 收到消息,返回确认,PUBACK报文是对QoS 1等级的PUBLISH报文的响应,PUBREC报文是对PUBLISH报文的响应
mqttMsgBack.publishAck(ctx, mqttMessage);
break;
case PUBREL:
// 释放消息,PUBREL报文是对QoS 2等级的PUBREC报文的响应,此时我们应该回应一个PUBCOMP报文
mqttMsgBack.publishComp(ctx, mqttMessage);
break;
default:
break;
}
}
}
}
/**
* @author: zhouwenjie
* @description: 对接收到的消息进行业务处理
* @create: 2023-04-07 16:29
* CONNECT 1 C->S 客户端请求与服务端建立连接 (服务端接收)
* CONNACK 2 S->C 服务端确认连接建立(客户端接收)
* PUBLISH 3 CóS 发布消息 (服务端接收【QoS 0级别,最多分发一次】)-->生产者只会发送一次消息,不关心消息是否被代理服务端或消费者收到
* PUBACK 4 CóS 收到发布消息确认(客户端接收【QoS 1级别,至少分发一次】) -->保证消息发送到服务端(也就是代理服务器broker),如果没收到或一定时间没收到服务端的ack,就会重发消息
* PUBREC 5 CóS 收到发布消息(客户端接收【QoS 2级别】)|
* PUBREL 6 CóS 释放发布消息(服务端接收【QoS 2级别】)|只分发一次消息,且保证到达 -->这三步保证消息有且仅有一次传递给消费者
* PUBCOMP 7 CóS 完成发布消息(客户端接收【QoS 2级别】)|
* SUBSCRIBE 8 C->S 订阅请求(服务端接收)
* SUBACK 9 S->C 订阅确认(客户端接收)
* UNSUBSCRIBE 10 C->S 取消订阅(服务端接收)
* UNSUBACK 11 S->C 取消订阅确认(客户端接收)
* PINGREQ 12 C->S 客户端发送PING(连接保活)命令(服务端接收)
* PINGRESP 13 S->C PING命令回复(客户端接收)
* DISCONNECT 14 C->S 断开连接 (服务端接收)
**/
@Slf4j
@Component
public class MqttMsgBack {
@Value("${driver.mqtt.wait_time}")
private long waitTime;
@Value("${driver.mqtt.topic_name}")
private String defaultTopicName;
@Value("${driver.mqtt.user_name}")
private String userName;
@Value("${driver.mqtt.password}")
private String password;
// 记录消息id的变量,id值范围1~65535
private final AtomicInteger nextMessageId = new AtomicInteger(1);
// ----------------------发送消息端(客户端)可能使用的方法----------------------------------------------------------------
/**
* 确认连接请求
*
* @param ctx
* @param mqttMessage
*/
public void receiveConnectionAck(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
MqttConnAckMessage mqttConnAckMessage = (MqttConnAckMessage) mqttMessage;
MqttConnAckVariableHeader variableHeader = mqttConnAckMessage.variableHeader();
MqttConnectReturnCode mqttConnectReturnCode = variableHeader.connectReturnCode();
if (mqttConnectReturnCode.name().equals(MqttConnectReturnCode.CONNECTION_ACCEPTED.name())) {
//连接成功
log.info("服务端连接验证成功");
// 订阅主题
this.subscribe(defaultTopicName, MqttQoS.AT_MOST_ONCE);
} else {
log.error("服务端连接验证失败:" + mqttConnectReturnCode.name());
}
}
/**
* 确认订阅回复
*
* @param ctx
* @param mqttMessage
*/
public void receiveSubAck(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
//删除消息重发机制
MqttMessageIdVariableHeader variableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader();
int messageId = variableHeader.messageId();
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(messageId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
/**
* 确认取消订阅回复
*
* @param ctx
* @param mqttMessage
*/
public void receiveUnSubAck(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
//删除消息重发机制
MqttMessageIdVariableHeader variableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader();
int messageId = variableHeader.messageId();
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(messageId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
/**
* 根据qos发布确认
*
* @param ctx
* @param mqttMessage
*/
public void receivePubAck(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
MqttFixedHeader fixedHeader = mqttMessage.fixedHeader();
MqttMessageType messageType = fixedHeader.messageType();
if (messageType == PUBACK) {
MqttMessageIdVariableHeader variableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader();
int messageId = variableHeader.messageId();
//等级为1的情况,直接删除原始消息,取消消息重发机制
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(messageId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
if (messageType == PUBREC) {
//等级为2的情况,收到PUBREC报文消息,先停止消息重发机制,再响应一个PUBREL报文并且构建消息重发机制
MqttPubReplyMessageVariableHeader variableHeader = (MqttPubReplyMessageVariableHeader) mqttMessage.variableHeader();
int messageId = variableHeader.messageId();
//构建返回报文,固定报头
MqttFixedHeader mqttFixedHeaderBack = new MqttFixedHeader(MqttMessageType.PUBREL, false, AT_LEAST_ONCE, false, 0);
//构建返回报文,可变报头
MqttPubReplyMessageVariableHeader mqttPubReplyMessageVariableHeader = new MqttPubReplyMessageVariableHeader(messageId, MqttPubReplyMessageVariableHeader.REASON_CODE_OK, MqttProperties.NO_PROPERTIES);
MqttMessage mqttMessageBack = new MqttMessage(mqttFixedHeaderBack, mqttPubReplyMessageVariableHeader);
ctx.writeAndFlush(mqttMessageBack);
//删除初始消息重发机制
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(messageId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
ctx.writeAndFlush(mqttMessageBack).addListener(future -> {
//构建消息重发
cachePubrelMsg(messageId, ctx);
});
}
}
private void cachePubrelMsg(int messageId, ChannelHandlerContext context) {
//缓存一份消息,规定时间内没有收到ack,用作重发,重发时将isDup设置为true,代表重复消息
//构建返回报文,固定报头
MqttFixedHeader mqttFixedHeaderBack = new MqttFixedHeader(MqttMessageType.PUBREL, false, AT_LEAST_ONCE, false, 0);
//构建返回报文,可变报头
MqttMessageIdVariableHeader mqttMessageIdVariableHeaderBack = MqttMessageIdVariableHeader.from(messageId);
MqttMessage mqttMessageBack = new MqttMessage(mqttFixedHeaderBack, mqttMessageIdVariableHeaderBack);
ScheduledFuture<?> scheduledFuture = TimerData.scheduledThreadPoolExecutor.scheduleAtFixedRate(new MonitorMsgTime(messageId, mqttMessageBack, context), waitTime, waitTime, TimeUnit.MILLISECONDS);
TimerData.scheduledFutureMap.put(messageId, scheduledFuture);
}
/**
* 功能描述: 接收到最后一次确认,取消上次PUBREL的消息重发机制
*
* @param ctx
* @param mqttMessage
* @return void
* @author zhouwenjie
* @date 2023/6/9 16:00
*/
public void receivePubcomp(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
MqttPubReplyMessageVariableHeader variableHeader = (MqttPubReplyMessageVariableHeader) mqttMessage.variableHeader();
int messageId = variableHeader.messageId();
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(messageId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
/**
* 心跳发送
*
* @param ctx
* @param mqttMessage
*/
public void pingReq(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
if (ctx != null && ctx.channel().isActive()) {
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PINGREQ, false, MqttQoS.AT_MOST_ONCE, false, 0);
MqttMessage mqttMessageBack = new MqttMessage(fixedHeader);
ctx.writeAndFlush(mqttMessageBack);
} else {
log.error("心跳提醒:服务端连接异常~");
}
}
public void connect(ChannelHandlerContext ctx) {
MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader("MQTT", 4, true, true, false, 0, false, true, 60);
String uuid = UUID.randomUUID().toString().replace("-", "");
MqttConnectPayload connectPayload = new MqttConnectPayload(uuid, null, null, userName, password.getBytes(CharsetUtil.UTF_8));
MqttFixedHeader mqttFixedHeaderInfo = new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_LEAST_ONCE, false, 0);
MqttConnectMessage connectMessage = new MqttConnectMessage(mqttFixedHeaderInfo, mqttConnectVariableHeader, connectPayload);
ctx.writeAndFlush(connectMessage);
}
/**
* 主动发送消息
*
* @param topic :主题名称
* @param payload :消息体
* @param qos : 服务质量等级
* @param retain :
* true:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
* false:仅仅为当前订阅者推送此消息。
*/
public void publish(String topic, ByteBuf payload, MqttQoS qos, boolean retain) {
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retain, 0);
MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId());
MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(fixedHeader, variableHeader, payload);
//将消息发送给订阅的客户端
ChannelHandlerContext context = ClientMqttHandler.context;
if (context != null && context.channel().isActive()) {
//因为ByteBuf每次发送之后就会被清空了,下次发送就拿不到payload,所以提前复制一份,客户端这里不用,因为在调用此方法的时候已经调用了Unpooled.wrappedBuffer了
payload.retainedDuplicate();
context.writeAndFlush(mqttPublishMessage);
if (qos == AT_LEAST_ONCE || qos == EXACTLY_ONCE) {
cachePublishMsg(qos, payload, variableHeader, fixedHeader, context);
}
}else {
log.error("发送消息提醒:服务端连接异常~");
}
}
private void cachePublishMsg(MqttQoS qos, ByteBuf byteBuf, MqttPublishVariableHeader variableHeader, MqttFixedHeader mqttFixedHeaderInfo, ChannelHandlerContext context) {
//缓存一份消息,规定时间内没有收到ack,用作重发,重发时将isDup设置为true,代表重复消息
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, true, qos, false, mqttFixedHeaderInfo.remainingLength());
MqttPublishMessage cachePubMessage = new MqttPublishMessage(fixedHeader, variableHeader, byteBuf);
ScheduledFuture<?> scheduledFuture = TimerData.scheduledThreadPoolExecutor.scheduleAtFixedRate(new MonitorMsgTime(variableHeader.packetId(), cachePubMessage, context), waitTime, waitTime, TimeUnit.MILLISECONDS);
TimerData.scheduledFutureMap.put(variableHeader.packetId(), scheduledFuture);
}
/**
* 订阅主题
*
* @param topicName :主题名称
* @param qos :服务端可以向此客户端发送的应用消息的最大QoS等级
*/
public void subscribe(String topicName, MqttQoS qos) {
if (StrUtil.isBlank(topicName)) {
topicName = defaultTopicName;
}
//构造固定头
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, AT_LEAST_ONCE, false, 0);
MqttMessageIdVariableHeader variableHeader = getNewMessageId();
//构造消息体,这里构建采用简单的模式(MqttTopicSubscription(String topicFilter, MqttQoS qualityOfService))
// 如果想用更复杂的,使用(MqttTopicSubscription(String topicFilter, MqttSubscriptionOption option))
MqttTopicSubscription subscription = new MqttTopicSubscription(topicName, qos);
MqttSubscribePayload payload = new MqttSubscribePayload(Collections.singletonList(subscription));
MqttSubscribeMessage mqttSubscribeMessage = new MqttSubscribeMessage(fixedHeader, variableHeader, payload);
ChannelHandlerContext context = ClientMqttHandler.context;
if (context != null && context.channel().isActive()) {
//发送消息,异步发送
context.writeAndFlush(mqttSubscribeMessage);
//缓存消息
MqttFixedHeader fixedHeader2 = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, true, AT_LEAST_ONCE, false, 0);
MqttSubscribeMessage mqttSubscribeMessage2 = new MqttSubscribeMessage(fixedHeader2, variableHeader, payload);
ScheduledFuture<?> scheduledFuture = TimerData.scheduledThreadPoolExecutor.scheduleAtFixedRate(new MonitorMsgTime(variableHeader.messageId(), mqttSubscribeMessage2, context), waitTime, waitTime, TimeUnit.MILLISECONDS);
TimerData.scheduledFutureMap.put(variableHeader.messageId(), scheduledFuture);
}else {
log.error("订阅提醒:服务端连接异常~");
}
}
/**
* 取消订阅主题
*
* @param topicName
*/
public void unsubscribe(String topicName) {
if (StrUtil.isBlank(topicName)) {
topicName = defaultTopicName;
}
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
MqttMessageIdVariableHeader variableHeader = getNewMessageId();
MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topicName));
MqttUnsubscribeMessage mqttUnsubscribeMessage = new MqttUnsubscribeMessage(fixedHeader, variableHeader, payload);
ChannelHandlerContext context = ClientMqttHandler.context;
if (context != null && context.channel().isActive()) {
//发送消息
context.writeAndFlush(mqttUnsubscribeMessage);
MqttFixedHeader fixedHeader2 = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, true, MqttQoS.AT_LEAST_ONCE, false, 0);
MqttUnsubscribeMessage mqttUnsubscribeMessage2 = new MqttUnsubscribeMessage(fixedHeader2, variableHeader, payload);
//缓存消息
ScheduledFuture<?> scheduledFuture = TimerData.scheduledThreadPoolExecutor.scheduleAtFixedRate(new MonitorMsgTime(variableHeader.messageId(), mqttUnsubscribeMessage2, context), waitTime, waitTime, TimeUnit.MILLISECONDS);
TimerData.scheduledFutureMap.put(variableHeader.messageId(), scheduledFuture);
}else {
log.error("取消订阅提醒:服务端连接异常~");
}
}
/**
* 功能描述: 获取消息id,int数,从1开始不能大于65535
*
* @param
* @return io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader
* @author zhouwenjie
* @date 2023/6/12 16:16
*/
private MqttMessageIdVariableHeader getNewMessageId() {
int messageId;
synchronized (this.nextMessageId) {
this.nextMessageId.compareAndSet(0xffff, 1);
messageId = this.nextMessageId.getAndIncrement();
}
return MqttMessageIdVariableHeader.from(messageId);
}
// ----------------------接收消息端(客户端)可能使用的方法----------------------------------------------------------------
/**
* 收到publish消息后的确认回复
* 根据qos发布确认
* isRetain:发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放
*
* @param ctx
* @param mqttMessage
*/
public void publishAck(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
MqttPublishMessage mqttPublishMessage = (MqttPublishMessage) mqttMessage;
MqttFixedHeader mqttFixedHeaderInfo = mqttPublishMessage.fixedHeader();
MqttQoS qos = mqttFixedHeaderInfo.qosLevel();
//返回消息给发送端
switch (qos) {
//至多一次
case AT_MOST_ONCE:
break;
//至少一次
case AT_LEAST_ONCE:
//构建返回报文, 可变报头
MqttMessageIdVariableHeader mqttMessageIdVariableHeaderBack = MqttMessageIdVariableHeader.from(mqttPublishMessage.variableHeader().packetId());
//构建返回报文, 固定报头
MqttFixedHeader mqttFixedHeaderBack = new MqttFixedHeader(PUBACK, mqttFixedHeaderInfo.isDup(), AT_MOST_ONCE, mqttFixedHeaderInfo.isRetain(), 0x02);
//构建PUBACK消息体
MqttPubAckMessage pubAck = new MqttPubAckMessage(mqttFixedHeaderBack, mqttMessageIdVariableHeaderBack);
ctx.writeAndFlush(pubAck);
break;
//刚好一次
case EXACTLY_ONCE:
//构建返回报文,固定报头
MqttFixedHeader mqttFixedHeaderBack2 = new MqttFixedHeader(MqttMessageType.PUBREC, false, AT_MOST_ONCE, false, 0x02);
//构建返回报文,可变报头
MqttPubReplyMessageVariableHeader mqttPubReplyMessageVariableHeader = new MqttPubReplyMessageVariableHeader(mqttPublishMessage.variableHeader().packetId(), MqttPubReplyMessageVariableHeader.REASON_CODE_OK, MqttProperties.NO_PROPERTIES);
MqttMessage mqttMessageBack = new MqttMessage(mqttFixedHeaderBack2, mqttPubReplyMessageVariableHeader);
ctx.writeAndFlush(mqttMessageBack);
break;
default:
break;
}
}
/**
* 发布完成 qos2
*
* @param ctx
* @param mqttMessage
*/
public void publishComp(ChannelHandlerContext ctx, MqttMessage mqttMessage) {
MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader();
//构建返回报文, 固定报头
MqttFixedHeader mqttFixedHeaderBack = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0x02);
//构建返回报文, 可变报头
MqttMessageIdVariableHeader mqttMessageIdVariableHeaderBack = MqttMessageIdVariableHeader.from(messageIdVariableHeader.messageId());
MqttMessage mqttMessageBack = new MqttMessage(mqttFixedHeaderBack, mqttMessageIdVariableHeaderBack);
ctx.writeAndFlush(mqttMessageBack);
}
}
/**
* @author: zhouwenjie
* @description: 轮询线程信息存储
* @create: 2021-04-29 08:06
**/
public class TimerData {
public static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
public static ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutureMap = new ConcurrentHashMap<>();
}
/**
* @author: zhouwenjie
* @description: 判断策略相关消息是否在规定时间段内发送,获取结束状态
* @create: 2021-01-07 16:09
**/
@Slf4j
public class MonitorMsgTime implements Runnable {
private Integer packetId;
private MqttMessage mqttMessage;
private ChannelHandlerContext ctx;
public MonitorMsgTime(Integer packetId, MqttMessage mqttMessage, ChannelHandlerContext ctx) {
this.packetId = packetId;
this.mqttMessage = mqttMessage;
this.ctx = ctx;
}
@Override
public void run() {
//注意,整个执行过程中,代码报错,线程就会终止
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
if (ctx != null && ctx.channel().isActive()) {
log.info("重复发送消息给服务端:" + address.getHostString());
if (mqttMessage instanceof MqttPublishMessage) {
//推送的原始消息,每次推送,都需要重新拷贝一份
try {
MqttPublishMessage mqttPublishMessage = (MqttPublishMessage) mqttMessage;
ByteBuf byteBuf = mqttPublishMessage.payload();
byteBuf.retainedDuplicate();
ctx.writeAndFlush(mqttMessage);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
} else {
//回复的ack类型消息
ctx.writeAndFlush(mqttMessage);
}
} else {
log.error(address.getHostString() + " 服务端断开,结束重复发送");
//如果离线了,就不发了
ScheduledFuture<?> scheduledFuture = TimerData.scheduledFutureMap.remove(packetId);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
}
}