Fix duplication of MQTT packets in MQTT Client
This commit is contained in:
parent
1affc60ace
commit
0468cf8cf5
@ -365,7 +365,8 @@ final class MqttClientImpl implements MqttClient {
|
|||||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retain, 0);
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retain, 0);
|
||||||
MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId());
|
MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId());
|
||||||
MqttPublishMessage message = new MqttPublishMessage(fixedHeader, variableHeader, payload);
|
MqttPublishMessage message = new MqttPublishMessage(fixedHeader, variableHeader, payload);
|
||||||
MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.packetId(), future, payload.retain(), message, qos);
|
MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.packetId(), future,
|
||||||
|
payload.retain(), message, qos, () -> !pendingPublishes.containsKey(variableHeader.packetId()));
|
||||||
this.pendingPublishes.put(pendingPublish.getMessageId(), pendingPublish);
|
this.pendingPublishes.put(pendingPublish.getMessageId(), pendingPublish);
|
||||||
ChannelFuture channelFuture = this.sendAndFlushPacket(message);
|
ChannelFuture channelFuture = this.sendAndFlushPacket(message);
|
||||||
|
|
||||||
@ -471,7 +472,8 @@ final class MqttClientImpl implements MqttClient {
|
|||||||
MqttSubscribePayload payload = new MqttSubscribePayload(Collections.singletonList(subscription));
|
MqttSubscribePayload payload = new MqttSubscribePayload(Collections.singletonList(subscription));
|
||||||
MqttSubscribeMessage message = new MqttSubscribeMessage(fixedHeader, variableHeader, payload);
|
MqttSubscribeMessage message = new MqttSubscribeMessage(fixedHeader, variableHeader, payload);
|
||||||
|
|
||||||
final MqttPendingSubscription pendingSubscription = new MqttPendingSubscription(future, topic, message);
|
final MqttPendingSubscription pendingSubscription = new MqttPendingSubscription(future, topic, message,
|
||||||
|
() -> !pendingSubscriptions.containsKey(variableHeader.messageId()));
|
||||||
pendingSubscription.addHandler(handler, once);
|
pendingSubscription.addHandler(handler, once);
|
||||||
this.pendingSubscriptions.put(variableHeader.messageId(), pendingSubscription);
|
this.pendingSubscriptions.put(variableHeader.messageId(), pendingSubscription);
|
||||||
this.pendingSubscribeTopics.add(topic);
|
this.pendingSubscribeTopics.add(topic);
|
||||||
@ -489,7 +491,8 @@ final class MqttClientImpl implements MqttClient {
|
|||||||
MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topic));
|
MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topic));
|
||||||
MqttUnsubscribeMessage message = new MqttUnsubscribeMessage(fixedHeader, variableHeader, payload);
|
MqttUnsubscribeMessage message = new MqttUnsubscribeMessage(fixedHeader, variableHeader, payload);
|
||||||
|
|
||||||
MqttPendingUnsubscription pendingUnsubscription = new MqttPendingUnsubscription(promise, topic, message);
|
MqttPendingUnsubscription pendingUnsubscription = new MqttPendingUnsubscription(promise, topic, message,
|
||||||
|
() -> !pendingServerUnsubscribes.containsKey(variableHeader.messageId()));
|
||||||
this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscription);
|
this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscription);
|
||||||
pendingUnsubscription.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
|
pendingUnsubscription.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import io.netty.util.concurrent.Promise;
|
|||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
final class MqttPendingPublish{
|
final class MqttPendingPublish {
|
||||||
|
|
||||||
private final int messageId;
|
private final int messageId;
|
||||||
private final Promise<Void> future;
|
private final Promise<Void> future;
|
||||||
@ -32,19 +32,21 @@ final class MqttPendingPublish{
|
|||||||
private final MqttPublishMessage message;
|
private final MqttPublishMessage message;
|
||||||
private final MqttQoS qos;
|
private final MqttQoS qos;
|
||||||
|
|
||||||
private final RetransmissionHandler<MqttPublishMessage> publishRetransmissionHandler = new RetransmissionHandler<>();
|
private final RetransmissionHandler<MqttPublishMessage> publishRetransmissionHandler;
|
||||||
private final RetransmissionHandler<MqttMessage> pubrelRetransmissionHandler = new RetransmissionHandler<>();
|
private final RetransmissionHandler<MqttMessage> pubrelRetransmissionHandler;
|
||||||
|
|
||||||
private boolean sent = false;
|
private boolean sent = false;
|
||||||
|
|
||||||
MqttPendingPublish(int messageId, Promise<Void> future, ByteBuf payload, MqttPublishMessage message, MqttQoS qos) {
|
MqttPendingPublish(int messageId, Promise<Void> future, ByteBuf payload, MqttPublishMessage message, MqttQoS qos, PendingOperation operation) {
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
this.future = future;
|
this.future = future;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.qos = qos;
|
this.qos = qos;
|
||||||
|
|
||||||
|
this.publishRetransmissionHandler = new RetransmissionHandler<>(operation);
|
||||||
this.publishRetransmissionHandler.setOriginalMessage(message);
|
this.publishRetransmissionHandler.setOriginalMessage(message);
|
||||||
|
this.pubrelRetransmissionHandler = new RetransmissionHandler<>(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getMessageId() {
|
int getMessageId() {
|
||||||
@ -99,8 +101,11 @@ final class MqttPendingPublish{
|
|||||||
this.pubrelRetransmissionHandler.stop();
|
this.pubrelRetransmissionHandler.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onChannelClosed(){
|
void onChannelClosed() {
|
||||||
this.publishRetransmissionHandler.stop();
|
this.publishRetransmissionHandler.stop();
|
||||||
this.pubrelRetransmissionHandler.stop();
|
this.pubrelRetransmissionHandler.stop();
|
||||||
|
if (payload != null) {
|
||||||
|
payload.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,22 +23,23 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
final class MqttPendingSubscription{
|
final class MqttPendingSubscription {
|
||||||
|
|
||||||
private final Promise<Void> future;
|
private final Promise<Void> future;
|
||||||
private final String topic;
|
private final String topic;
|
||||||
private final Set<MqttPendingHandler> handlers = new HashSet<>();
|
private final Set<MqttPendingHandler> handlers = new HashSet<>();
|
||||||
private final MqttSubscribeMessage subscribeMessage;
|
private final MqttSubscribeMessage subscribeMessage;
|
||||||
|
|
||||||
private final RetransmissionHandler<MqttSubscribeMessage> retransmissionHandler = new RetransmissionHandler<>();
|
private final RetransmissionHandler<MqttSubscribeMessage> retransmissionHandler;
|
||||||
|
|
||||||
private boolean sent = false;
|
private boolean sent = false;
|
||||||
|
|
||||||
MqttPendingSubscription(Promise<Void> future, String topic, MqttSubscribeMessage message) {
|
MqttPendingSubscription(Promise<Void> future, String topic, MqttSubscribeMessage message, PendingOperation operation) {
|
||||||
this.future = future;
|
this.future = future;
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
this.subscribeMessage = message;
|
this.subscribeMessage = message;
|
||||||
|
|
||||||
|
this.retransmissionHandler = new RetransmissionHandler<>(operation);
|
||||||
this.retransmissionHandler.setOriginalMessage(message);
|
this.retransmissionHandler.setOriginalMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ final class MqttPendingSubscription{
|
|||||||
return subscribeMessage;
|
return subscribeMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHandler(MqttHandler handler, boolean once){
|
void addHandler(MqttHandler handler, boolean once) {
|
||||||
this.handlers.add(new MqttPendingHandler(handler, once));
|
this.handlers.add(new MqttPendingHandler(handler, once));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,14 +72,14 @@ final class MqttPendingSubscription{
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startRetransmitTimer(EventLoop eventLoop, Consumer<Object> sendPacket) {
|
void startRetransmitTimer(EventLoop eventLoop, Consumer<Object> sendPacket) {
|
||||||
if(this.sent){ //If the packet is sent, we can start the retransmit timer
|
if (this.sent) { //If the packet is sent, we can start the retransmit timer
|
||||||
this.retransmissionHandler.setHandle((fixedHeader, originalMessage) ->
|
this.retransmissionHandler.setHandle((fixedHeader, originalMessage) ->
|
||||||
sendPacket.accept(new MqttSubscribeMessage(fixedHeader, originalMessage.variableHeader(), originalMessage.payload())));
|
sendPacket.accept(new MqttSubscribeMessage(fixedHeader, originalMessage.variableHeader(), originalMessage.payload())));
|
||||||
this.retransmissionHandler.start(eventLoop);
|
this.retransmissionHandler.start(eventLoop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSubackReceived(){
|
void onSubackReceived() {
|
||||||
this.retransmissionHandler.stop();
|
this.retransmissionHandler.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ final class MqttPendingSubscription{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onChannelClosed(){
|
void onChannelClosed() {
|
||||||
this.retransmissionHandler.stop();
|
this.retransmissionHandler.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,12 +26,13 @@ final class MqttPendingUnsubscription{
|
|||||||
private final Promise<Void> future;
|
private final Promise<Void> future;
|
||||||
private final String topic;
|
private final String topic;
|
||||||
|
|
||||||
private final RetransmissionHandler<MqttUnsubscribeMessage> retransmissionHandler = new RetransmissionHandler<>();
|
private final RetransmissionHandler<MqttUnsubscribeMessage> retransmissionHandler;
|
||||||
|
|
||||||
MqttPendingUnsubscription(Promise<Void> future, String topic, MqttUnsubscribeMessage unsubscribeMessage) {
|
MqttPendingUnsubscription(Promise<Void> future, String topic, MqttUnsubscribeMessage unsubscribeMessage, PendingOperation operation) {
|
||||||
this.future = future;
|
this.future = future;
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
|
|
||||||
|
this.retransmissionHandler = new RetransmissionHandler<>(operation);
|
||||||
this.retransmissionHandler.setOriginalMessage(unsubscribeMessage);
|
this.retransmissionHandler.setOriginalMessage(unsubscribeMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,10 +28,10 @@ final class MqttSubscription {
|
|||||||
private boolean called;
|
private boolean called;
|
||||||
|
|
||||||
MqttSubscription(String topic, MqttHandler handler, boolean once) {
|
MqttSubscription(String topic, MqttHandler handler, boolean once) {
|
||||||
if(topic == null){
|
if (topic == null) {
|
||||||
throw new NullPointerException("topic");
|
throw new NullPointerException("topic");
|
||||||
}
|
}
|
||||||
if(handler == null){
|
if (handler == null) {
|
||||||
throw new NullPointerException("handler");
|
throw new NullPointerException("handler");
|
||||||
}
|
}
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
@ -56,7 +56,7 @@ final class MqttSubscription {
|
|||||||
return called;
|
return called;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean matches(String topic){
|
boolean matches(String topic) {
|
||||||
return this.topicRegex.matcher(topic).matches();
|
return this.topicRegex.matcher(topic).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2021 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.mqtt;
|
||||||
|
|
||||||
|
public interface PendingOperation {
|
||||||
|
|
||||||
|
boolean isCanceled();
|
||||||
|
|
||||||
|
}
|
||||||
@ -21,33 +21,43 @@ import io.netty.handler.codec.mqtt.MqttMessage;
|
|||||||
import io.netty.handler.codec.mqtt.MqttMessageType;
|
import io.netty.handler.codec.mqtt.MqttMessageType;
|
||||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||||
import io.netty.util.concurrent.ScheduledFuture;
|
import io.netty.util.concurrent.ScheduledFuture;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
final class RetransmissionHandler<T extends MqttMessage> {
|
final class RetransmissionHandler<T extends MqttMessage> {
|
||||||
|
|
||||||
|
private volatile boolean stopped;
|
||||||
|
private final PendingOperation pendingOperation;
|
||||||
private ScheduledFuture<?> timer;
|
private ScheduledFuture<?> timer;
|
||||||
private int timeout = 10;
|
private int timeout = 10;
|
||||||
private BiConsumer<MqttFixedHeader, T> handler;
|
private BiConsumer<MqttFixedHeader, T> handler;
|
||||||
private T originalMessage;
|
private T originalMessage;
|
||||||
|
|
||||||
void start(EventLoop eventLoop){
|
void start(EventLoop eventLoop) {
|
||||||
if(eventLoop == null){
|
if (eventLoop == null) {
|
||||||
throw new NullPointerException("eventLoop");
|
throw new NullPointerException("eventLoop");
|
||||||
}
|
}
|
||||||
if(this.handler == null){
|
if (this.handler == null) {
|
||||||
throw new NullPointerException("handler");
|
throw new NullPointerException("handler");
|
||||||
}
|
}
|
||||||
this.timeout = 10;
|
this.timeout = 10;
|
||||||
this.startTimer(eventLoop);
|
this.startTimer(eventLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startTimer(EventLoop eventLoop){
|
private void startTimer(EventLoop eventLoop) {
|
||||||
|
if (stopped || pendingOperation.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.timer = eventLoop.schedule(() -> {
|
this.timer = eventLoop.schedule(() -> {
|
||||||
|
if (stopped || pendingOperation.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.timeout += 5;
|
this.timeout += 5;
|
||||||
boolean isDup = this.originalMessage.fixedHeader().isDup();
|
boolean isDup = this.originalMessage.fixedHeader().isDup();
|
||||||
if(this.originalMessage.fixedHeader().messageType() == MqttMessageType.PUBLISH && this.originalMessage.fixedHeader().qosLevel() != MqttQoS.AT_MOST_ONCE){
|
if (this.originalMessage.fixedHeader().messageType() == MqttMessageType.PUBLISH && this.originalMessage.fixedHeader().qosLevel() != MqttQoS.AT_MOST_ONCE) {
|
||||||
isDup = true;
|
isDup = true;
|
||||||
}
|
}
|
||||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(this.originalMessage.fixedHeader().messageType(), isDup, this.originalMessage.fixedHeader().qosLevel(), this.originalMessage.fixedHeader().isRetain(), this.originalMessage.fixedHeader().remainingLength());
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(this.originalMessage.fixedHeader().messageType(), isDup, this.originalMessage.fixedHeader().qosLevel(), this.originalMessage.fixedHeader().isRetain(), this.originalMessage.fixedHeader().remainingLength());
|
||||||
@ -56,8 +66,9 @@ final class RetransmissionHandler<T extends MqttMessage> {
|
|||||||
}, timeout, TimeUnit.SECONDS);
|
}, timeout, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop(){
|
void stop() {
|
||||||
if(this.timer != null){
|
stopped = true;
|
||||||
|
if (this.timer != null) {
|
||||||
this.timer.cancel(true);
|
this.timer.cancel(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user