mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-19 19:13:11 +08:00
Fix issues in RPC extension.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MQTTnet.Extensions.Rpc</id>
|
<id>MQTTnet.Extensions.Rpc</id>
|
||||||
<version>2.6.0</version>
|
<version>2.7.4</version>
|
||||||
<authors>Christian Kratky</authors>
|
<authors>Christian Kratky</authors>
|
||||||
<owners>Christian Kratky</owners>
|
<owners>Christian Kratky</owners>
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||||
@@ -12,36 +12,42 @@
|
|||||||
<description>This is a extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
<description>This is a extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||||
<releaseNotes>* Initial version.
|
<releaseNotes>* Initial version.
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="netstandard2.0">
|
<group targetFramework="netstandard2.0">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
<group targetFramework="netstandard1.3">
|
<group targetFramework="netstandard1.3">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="netstandard2.0">
|
<group targetFramework="netstandard2.0">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="uap10.0">
|
<group targetFramework="uap10.0">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="net452">
|
<group targetFramework="net452">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="net461">
|
<group targetFramework="net461">
|
||||||
<dependency id="MQTTnet" version="2.6.0-rc1" />
|
<dependency id="MQTTnet" version="2.7.4" />
|
||||||
</group>
|
</group>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
||||||
<files>
|
<files>
|
||||||
|
<!-- .NET Standard 1.3 -->
|
||||||
|
<file src="..\Extensions\MQTTnet.Extensions.Rpc\bin\Release\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||||
|
|
||||||
<!-- .NET Standard 2.0 -->
|
<!-- .NET Standard 2.0 -->
|
||||||
<file src="..\Frameworks\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
<file src="..\Extensions\MQTTnet.Extensions.Rpc\bin\Release\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
|
||||||
|
|
||||||
|
<!-- Universal Windows -->
|
||||||
|
<file src="..\Extensions\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
|
||||||
|
|
||||||
|
<!-- .NET Framework -->
|
||||||
|
<file src="..\Extensions\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
|
||||||
|
<file src="..\Extensions\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
+2
-16
@@ -2,7 +2,7 @@
|
|||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MQTTnet</id>
|
<id>MQTTnet</id>
|
||||||
<version>2.7.4</version>
|
<version>2.7.5</version>
|
||||||
<authors>Christian Kratky</authors>
|
<authors>Christian Kratky</authors>
|
||||||
<owners>Christian Kratky</owners>
|
<owners>Christian Kratky</owners>
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||||
@@ -10,47 +10,34 @@
|
|||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||||
<releaseNotes> * [Client] Fixed a deadlock while the client disconnects.
|
<releaseNotes> * [Client] Added new overloads for the message builder.
|
||||||
* [Client] Fixed broken support for protocol version 3.1.0.
|
|
||||||
* [Server] The _MqttTcpServerAdapter_ is now added to the ASP.NET services.
|
|
||||||
* [Server] _MqttServerAdapter_ is renamed to _MqttTcpServerAdapter_ (BREAKING CHANGE!).
|
|
||||||
* [Server] The server no longer sends the will message of a client if the disconnect was clean (via _Disconnect_ packet).
|
|
||||||
* [Server] The application message interceptor now allows closing the connection.
|
|
||||||
* [Server] Added a new flag for the _ClientDisconnected_ event which contains a value indicating whether the disconnect was clean (via _Disconnect_ packet).
|
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<group targetFramework="netstandard1.3">
|
<group targetFramework="netstandard1.3">
|
||||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||||
<dependency id="System.Net.Security" version="4.3.2" />
|
<dependency id="System.Net.Security" version="4.3.2" />
|
||||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="netstandard2.0">
|
<group targetFramework="netstandard2.0">
|
||||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||||
<dependency id="System.Net.Security" version="4.3.2" />
|
<dependency id="System.Net.Security" version="4.3.2" />
|
||||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="uap10.0">
|
<group targetFramework="uap10.0">
|
||||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.1" />
|
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.1" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="net452">
|
<group targetFramework="net452">
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group targetFramework="net461">
|
<group targetFramework="net461">
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
||||||
<files>
|
<files>
|
||||||
|
|
||||||
<!-- .NET Standard 1.3 -->
|
<!-- .NET Standard 1.3 -->
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||||
|
|
||||||
@@ -63,6 +50,5 @@
|
|||||||
<!-- .NET Framework -->
|
<!-- .NET Framework -->
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
|
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
|
||||||
|
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
@@ -9,10 +10,8 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
public sealed class MqttRpcClient : IDisposable
|
public sealed class MqttRpcClient : IDisposable
|
||||||
{
|
{
|
||||||
private const string ResponseTopic = "$MQTTnet.RPC/+/+/response";
|
|
||||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||||
private readonly IMqttClient _mqttClient;
|
private readonly IMqttClient _mqttClient;
|
||||||
private bool _isEnabled;
|
|
||||||
|
|
||||||
public MqttRpcClient(IMqttClient mqttClient)
|
public MqttRpcClient(IMqttClient mqttClient)
|
||||||
{
|
{
|
||||||
@@ -21,16 +20,9 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EnableAsync()
|
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
await _mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(ResponseTopic).WithAtLeastOnceQoS().Build());
|
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel);
|
||||||
_isEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisableAsync()
|
|
||||||
{
|
|
||||||
await _mqttClient.UnsubscribeAsync(ResponseTopic);
|
|
||||||
_isEnabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
@@ -42,12 +34,7 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isEnabled)
|
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The RPC client is not enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestTopic = $"$MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
|
|
||||||
var responseTopic = requestTopic + "/response";
|
var responseTopic = requestTopic + "/response";
|
||||||
|
|
||||||
var requestMessage = new MqttApplicationMessageBuilder()
|
var requestMessage = new MqttApplicationMessageBuilder()
|
||||||
@@ -64,18 +51,21 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _mqttClient.PublishAsync(requestMessage);
|
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false);
|
||||||
|
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false);
|
||||||
|
|
||||||
return await tcs.Task.TimeoutAfter(timeout);
|
return await tcs.Task.TimeoutAfter(timeout);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_waitingCalls.TryRemove(responseTopic, out _);
|
_waitingCalls.TryRemove(responseTopic, out _);
|
||||||
|
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out TaskCompletionSource<byte[]> tcs))
|
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,28 @@
|
|||||||
|
// If using the MQTT client PubSubClient it must be ensured that the request topic for each method is subscribed like the following.
|
||||||
|
_mqttClient.subscribe("MQTTnet.RPC/+/ping");
|
||||||
|
_mqttClient.subscribe("MQTTnet.RPC/+/do_something");
|
||||||
|
|
||||||
|
// It is not allowed to change the structure of the topic. Otherwise RPC will not work. So method names can be separated using
|
||||||
|
// an _ or . but no +, # or /.. If it is required to distinguish between devices own rules can be defined like the following.
|
||||||
|
_mqttClient.subscribe("MQTTnet.RPC/+/deviceA.ping");
|
||||||
|
_mqttClient.subscribe("MQTTnet.RPC/+/deviceB.ping");
|
||||||
|
_mqttClient.subscribe("MQTTnet.RPC/+/deviceC.getTemperature");
|
||||||
|
|
||||||
|
// Within the callback of the mqtt client the topic must be checked if it belongs to MQTTnet RPC. The following code shows one
|
||||||
|
// possible way.
|
||||||
|
void mqtt_Callback(char *topic, byte *payload, unsigned int payloadLength)
|
||||||
|
{
|
||||||
|
String topicString = String(topic);
|
||||||
|
|
||||||
|
if (topicString.startsWith("MQTTnet.RPC/")) {
|
||||||
|
String responseTopic = topicString + String("/response");
|
||||||
|
|
||||||
|
if (topicString.endsWith("/deviceA.ping")) {
|
||||||
|
mqtt_publish(responseTopic, "pong", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not send response message with the _retain_ flag set to true.
|
||||||
|
// All required data for a RPC call and the result must be placed into the payload.
|
||||||
@@ -32,7 +32,12 @@ namespace MQTTnet
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithPayload(MemoryStream payload)
|
public MqttApplicationMessageBuilder WithPayload(Stream payload)
|
||||||
|
{
|
||||||
|
return WithPayload(payload, payload.Length - payload.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithPayload(Stream payload, long length)
|
||||||
{
|
{
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
{
|
{
|
||||||
@@ -46,7 +51,7 @@ namespace MQTTnet
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_payload = new byte[payload.Length - payload.Position];
|
_payload = new byte[length];
|
||||||
payload.Read(_payload, 0, _payload.Length);
|
payload.Read(_payload, 0, _payload.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace MQTTnet.Packets
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var topicFiltersText = string.Join(",", TopicFilters);
|
var topicFiltersText = string.Join(",", TopicFilters);
|
||||||
return "Subscribe: [PacketIdentifier=" + PacketIdentifier + "] [TopicFilters=" + topicFiltersText + "]";
|
return "Unsubscribe: [PacketIdentifier=" + PacketIdentifier + "] [TopicFilters=" + topicFiltersText + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1
|
|||||||
appveyor.yml = appveyor.yml
|
appveyor.yml = appveyor.yml
|
||||||
Build\build.ps1 = Build\build.ps1
|
Build\build.ps1 = Build\build.ps1
|
||||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||||
|
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
@@ -350,13 +350,10 @@ namespace MQTTnet.TestApp.UniversalWindows
|
|||||||
payload = Convert.FromBase64String(RpcPayload.Text);
|
payload = Convert.FromBase64String(RpcPayload.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var rpcClient = new MqttRpcClient(_mqttClient);
|
var rpcClient = new MqttRpcClient(_mqttClient);
|
||||||
await rpcClient.EnableAsync();
|
|
||||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), RpcMethod.Text, payload, qos);
|
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), RpcMethod.Text, payload, qos);
|
||||||
await rpcClient.DisableAsync();
|
|
||||||
|
|
||||||
RpcResponses.Items.Add(RpcMethod.Text + " >>> " + Encoding.UTF8.GetString(response));
|
RpcResponses.Items.Add(RpcMethod.Text + " >>> " + Encoding.UTF8.GetString(response));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user