diff --git a/Source/MQTTnet/Client/Options/MqttClientOptions.cs b/Source/MQTTnet/Client/Options/MqttClientOptions.cs
index ce5aed59..931ece6f 100644
--- a/Source/MQTTnet/Client/Options/MqttClientOptions.cs
+++ b/Source/MQTTnet/Client/Options/MqttClientOptions.cs
@@ -98,6 +98,16 @@ namespace MQTTnet.Client
///
public ushort TopicAliasMaximum { get; set; }
+ ///
+ /// If set to true, the bridge will attempt to indicate to the remote broker that it is a bridge not an ordinary
+ /// client.
+ /// If successful, this means that loop detection will be more effective and that retained messages will be propagated
+ /// correctly.
+ /// Not all brokers support this feature so it may be necessary to set it to false if your bridge does not connect
+ /// properly.
+ ///
+ public bool TryPrivate { get; set; } = true;
+
///
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
index 4896d2b6..3ed60a24 100644
--- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
+++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
@@ -96,16 +96,6 @@ namespace MQTTnet.Client
return this;
}
- ///
- /// Sets the timeout which will be applied at socket level and internal operations.
- /// The default value is the same as for sockets in .NET in general.
- ///
- public MqttClientOptionsBuilder WithTimeout(TimeSpan value)
- {
- _options.Timeout = value;
- return this;
- }
-
public MqttClientOptionsBuilder WithConnectionUri(Uri uri)
{
if (uri == null)
@@ -289,6 +279,16 @@ namespace MQTTnet.Client
return this;
}
+ ///
+ /// Sets the timeout which will be applied at socket level and internal operations.
+ /// The default value is the same as for sockets in .NET in general.
+ ///
+ public MqttClientOptionsBuilder WithTimeout(TimeSpan value)
+ {
+ _options.Timeout = value;
+ return this;
+ }
+
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
{
_tlsParameters = parameters;
@@ -322,6 +322,20 @@ namespace MQTTnet.Client
return this;
}
+ ///
+ /// If set to true, the bridge will attempt to indicate to the remote broker that it is a bridge not an ordinary
+ /// client.
+ /// If successful, this means that loop detection will be more effective and that retained messages will be propagated
+ /// correctly.
+ /// Not all brokers support this feature so it may be necessary to set it to false if your bridge does not connect
+ /// properly.
+ ///
+ public MqttClientOptionsBuilder WithTryPrivate(bool tryPrivate = true)
+ {
+ _options.TryPrivate = true;
+ return this;
+ }
+
public MqttClientOptionsBuilder WithUserProperty(string name, string value)
{
if (name is null)
diff --git a/Source/MQTTnet/Formatter/MqttConnectPacketFactory.cs b/Source/MQTTnet/Formatter/MqttConnectPacketFactory.cs
index d818630a..09096b68 100644
--- a/Source/MQTTnet/Formatter/MqttConnectPacketFactory.cs
+++ b/Source/MQTTnet/Formatter/MqttConnectPacketFactory.cs
@@ -33,7 +33,8 @@ namespace MQTTnet.Formatter
RequestResponseInformation = clientOptions.RequestResponseInformation,
SessionExpiryInterval = clientOptions.SessionExpiryInterval,
TopicAliasMaximum = clientOptions.TopicAliasMaximum,
- UserProperties = clientOptions.UserProperties
+ UserProperties = clientOptions.UserProperties,
+ TryPrivate = clientOptions.TryPrivate
};
if (!string.IsNullOrEmpty(clientOptions.WillTopic))
diff --git a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs
index 2dcf00be..580c59b3 100644
--- a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs
+++ b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs
@@ -95,6 +95,10 @@ namespace MQTTnet.Formatter
var protocolName = _bufferReader.ReadString();
var protocolLevel = _bufferReader.ReadByte();
+ // Remove the mosquitto try_private flag (MQTT 3.1.1 Bridge).
+ // This flag is accepted but not yet used.
+ protocolLevel &= 0x7F;
+
if (protocolName == "MQTT")
{
if (protocolLevel == 5)
diff --git a/Source/MQTTnet/Formatter/V3/MqttV3PacketFormatter.cs b/Source/MQTTnet/Formatter/V3/MqttV3PacketFormatter.cs
index d8276bb6..7d315433 100644
--- a/Source/MQTTnet/Formatter/V3/MqttV3PacketFormatter.cs
+++ b/Source/MQTTnet/Formatter/V3/MqttV3PacketFormatter.cs
@@ -175,12 +175,18 @@ namespace MQTTnet.Formatter.V3
throw new MqttProtocolViolationException("MQTT protocol name do not match MQTT v3.");
}
+ var tryPrivate = (protocolVersion & 0x80) > 0;
+ protocolVersion &= 0x7F;
+
if (protocolVersion != 3 && protocolVersion != 4)
{
throw new MqttProtocolViolationException("MQTT protocol version do not match MQTT v3.");
}
- var packet = new MqttConnectPacket();
+ var packet = new MqttConnectPacket
+ {
+ TryPrivate = tryPrivate
+ };
var connectFlags = _bufferReader.ReadByte();
if ((connectFlags & 0x1) > 0)
@@ -409,7 +415,14 @@ namespace MQTTnet.Formatter.V3
ValidateConnectPacket(packet);
bufferWriter.WriteString("MQIsdp");
- bufferWriter.WriteByte(3); // Protocol Level 3
+
+ var protocolVersion = 3;
+ if (packet.TryPrivate)
+ {
+ protocolVersion |= 0x80;
+ }
+
+ bufferWriter.WriteByte((byte)protocolVersion);
byte connectFlags = 0x0;
if (packet.CleanSession)
diff --git a/Source/MQTTnet/Packets/MqttConnectPacket.cs b/Source/MQTTnet/Packets/MqttConnectPacket.cs
index a543f5b8..f9e83f35 100644
--- a/Source/MQTTnet/Packets/MqttConnectPacket.cs
+++ b/Source/MQTTnet/Packets/MqttConnectPacket.cs
@@ -64,6 +64,8 @@ namespace MQTTnet.Packets
public List WillUserProperties { get; set; }
+ public bool TryPrivate { get; set; }
+
public override string ToString()
{
var passwordText = string.Empty;