@@ -10,9 +10,14 @@ | |||||
<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>* Merged projects. | |||||
* Added a strong name for the assembly. | |||||
<releaseNotes>* [Core] Merged projects (BREAKING CHANGE! But only namespace changes). | |||||
* [Core] Added a strong name for the assembly. | |||||
* [Core] Performance optimizations. | |||||
* [Core] Fixed a logging issue when dealing with IOExceptions. | |||||
* [Client] Fixed an issue in _ManagedClient_ which can cause the client to stop when publishing subscriptions. | |||||
* [Server] The application message interceptor can now delete any received application message. | |||||
* [Server] Added a ConnectionValidator context to align with other APIs. | |||||
* [Server] Added an interface for the _MqttServerOptions_. | |||||
</releaseNotes> | </releaseNotes> | ||||
<copyright>Copyright Christian Kratky 2016-2017</copyright> | <copyright>Copyright Christian Kratky 2016-2017</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> | ||||
@@ -1,6 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Net.Sockets; | |||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -171,13 +172,25 @@ namespace MQTTnet.Adapter | |||||
} | } | ||||
catch (COMException comException) | catch (COMException comException) | ||||
{ | { | ||||
if ((uint)comException.HResult == ErrorOperationAborted) | |||||
if ((uint) comException.HResult == ErrorOperationAborted) | |||||
{ | { | ||||
throw new OperationCanceledException(); | throw new OperationCanceledException(); | ||||
} | } | ||||
throw new MqttCommunicationException(comException); | throw new MqttCommunicationException(comException); | ||||
} | } | ||||
catch (IOException exception) | |||||
{ | |||||
if (exception.InnerException is SocketException socketException) | |||||
{ | |||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted) | |||||
{ | |||||
throw new OperationCanceledException(); | |||||
} | |||||
} | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
catch (Exception exception) | catch (Exception exception) | ||||
{ | { | ||||
throw new MqttCommunicationException(exception); | throw new MqttCommunicationException(exception); | ||||
@@ -142,33 +142,53 @@ namespace MQTTnet.ManagedClient | |||||
{ | { | ||||
while (!cancellationToken.IsCancellationRequested) | while (!cancellationToken.IsCancellationRequested) | ||||
{ | { | ||||
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false); | |||||
if (connectionState == ReconnectionResult.NotConnected) | |||||
{ | |||||
_publishingCancellationToken?.Cancel(false); | |||||
_publishingCancellationToken = null; | |||||
await TryMaintainConnectionAsync(cancellationToken); | |||||
} | |||||
} | |||||
catch (OperationCanceledException) | |||||
{ | |||||
} | |||||
catch (Exception exception) | |||||
{ | |||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection."); | |||||
} | |||||
finally | |||||
{ | |||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false); | |||||
_logger.Info<ManagedMqttClient>("Stopped"); | |||||
} | |||||
} | |||||
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false); | |||||
continue; | |||||
} | |||||
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken) | |||||
{ | |||||
try | |||||
{ | |||||
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false); | |||||
if (connectionState == ReconnectionResult.NotConnected) | |||||
{ | |||||
_publishingCancellationToken?.Cancel(false); | |||||
_publishingCancellationToken = null; | |||||
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed) | |||||
{ | |||||
await PushSubscriptionsAsync(); | |||||
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed) | |||||
{ | |||||
await PushSubscriptionsAsync().ConfigureAwait(false); | |||||
_publishingCancellationToken = new CancellationTokenSource(); | |||||
_publishingCancellationToken = new CancellationTokenSource(); | |||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
Task.Run(async () => await PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.Token).ConfigureAwait(false); | |||||
Task.Run(async () => await PublishQueuedMessagesAsync(_publishingCancellationToken.Token).ConfigureAwait(false), _publishingCancellationToken.Token).ConfigureAwait(false); | |||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed | ||||
continue; | |||||
} | |||||
return; | |||||
} | |||||
if (connectionState == ReconnectionResult.StillConnected) | |||||
{ | |||||
await Task.Delay(TimeSpan.FromSeconds(1), _connectionCancellationToken.Token).ConfigureAwait(false); | |||||
} | |||||
if (connectionState == ReconnectionResult.StillConnected) | |||||
{ | |||||
await Task.Delay(TimeSpan.FromSeconds(1), _connectionCancellationToken.Token).ConfigureAwait(false); | |||||
} | } | ||||
} | } | ||||
catch (OperationCanceledException) | catch (OperationCanceledException) | ||||
@@ -182,11 +202,6 @@ namespace MQTTnet.ManagedClient | |||||
{ | { | ||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection."); | _logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection."); | ||||
} | } | ||||
finally | |||||
{ | |||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false); | |||||
_logger.Info<ManagedMqttClient>("Stopped"); | |||||
} | |||||
} | } | ||||
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken) | private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken) | ||||
@@ -206,16 +221,19 @@ namespace MQTTnet.ManagedClient | |||||
continue; | continue; | ||||
} | } | ||||
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false); | |||||
await _storageManager.RemoveAsync(message).ConfigureAwait(false); | |||||
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false); | |||||
} | } | ||||
} | } | ||||
catch (OperationCanceledException) | catch (OperationCanceledException) | ||||
{ | { | ||||
} | } | ||||
catch (Exception exception) | |||||
{ | |||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application messages."); | |||||
} | |||||
finally | finally | ||||
{ | { | ||||
_logger.Info<ManagedMqttClient>("Stopped publishing messages"); | |||||
_logger.Trace<ManagedMqttClient>("Stopped publishing messages."); | |||||
} | } | ||||
} | } | ||||
@@ -224,6 +242,7 @@ namespace MQTTnet.ManagedClient | |||||
try | try | ||||
{ | { | ||||
await _mqttClient.PublishAsync(message).ConfigureAwait(false); | await _mqttClient.PublishAsync(message).ConfigureAwait(false); | ||||
await _storageManager.RemoveAsync(message).ConfigureAwait(false); | |||||
} | } | ||||
catch (MqttCommunicationException exception) | catch (MqttCommunicationException exception) | ||||
{ | { | ||||
@@ -1,4 +1,5 @@ | |||||
using MQTTnet.Serializer; | |||||
using System; | |||||
using MQTTnet.Serializer; | |||||
namespace MQTTnet.Server | namespace MQTTnet.Server | ||||
{ | { | ||||
@@ -7,5 +8,9 @@ namespace MQTTnet.Server | |||||
public string ClientId { get; set; } | public string ClientId { get; set; } | ||||
public MqttProtocolVersion ProtocolVersion { get; set; } | public MqttProtocolVersion ProtocolVersion { get; set; } | ||||
public TimeSpan LastPacketReceivedDuration { get; set; } | |||||
public TimeSpan LastNonKeepAlivePacketReceivedDuration{ get; set; } | |||||
} | } | ||||
} | } |
@@ -120,6 +120,7 @@ namespace MQTTnet.Server | |||||
await _sessionsSemaphore.WaitAsync().ConfigureAwait(false); | await _sessionsSemaphore.WaitAsync().ConfigureAwait(false); | ||||
try | try | ||||
{ | { | ||||
var now = DateTime.UtcNow; | |||||
return _sessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient | return _sessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient | ||||
{ | { | ||||
ClientId = s.Value.ClientId, | ClientId = s.Value.ClientId, | ||||
@@ -136,13 +137,21 @@ namespace MQTTnet.Server | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var interceptorContext = new MqttApplicationMessageInterceptorContext | |||||
if (_options.ApplicationMessageInterceptor != null) | |||||
{ | { | ||||
ApplicationMessage = applicationMessage | |||||
}; | |||||
var interceptorContext = new MqttApplicationMessageInterceptorContext | |||||
{ | |||||
ApplicationMessage = applicationMessage | |||||
}; | |||||
_options.ApplicationMessageInterceptor(interceptorContext); | |||||
applicationMessage = interceptorContext.ApplicationMessage; | |||||
} | |||||
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext); | |||||
applicationMessage = interceptorContext.ApplicationMessage; | |||||
if (applicationMessage == null) | |||||
{ | |||||
return; | |||||
} | |||||
if (applicationMessage.Retain) | if (applicationMessage.Retain) | ||||
{ | { | ||||
@@ -14,6 +14,17 @@ namespace MQTTnet.TestApp.NetCore | |||||
MqttNetGlobalLogger.LogMessagePublished += PrintToConsole; | MqttNetGlobalLogger.LogMessagePublished += PrintToConsole; | ||||
} | } | ||||
public static void PrintToConsole(string message, ConsoleColor color) | |||||
{ | |||||
lock (Lock) | |||||
{ | |||||
var backupColor = Console.ForegroundColor; | |||||
Console.ForegroundColor = color; | |||||
Console.Write(message); | |||||
Console.ForegroundColor = backupColor; | |||||
} | |||||
} | |||||
private static void PrintToConsole(object sender, MqttNetLogMessagePublishedEventArgs e) | private static void PrintToConsole(object sender, MqttNetLogMessagePublishedEventArgs e) | ||||
{ | { | ||||
var output = new StringBuilder(); | var output = new StringBuilder(); | ||||
@@ -23,28 +34,24 @@ namespace MQTTnet.TestApp.NetCore | |||||
output.AppendLine(e.TraceMessage.Exception.ToString()); | output.AppendLine(e.TraceMessage.Exception.ToString()); | ||||
} | } | ||||
lock (Lock) | |||||
var color = ConsoleColor.Red; | |||||
switch (e.TraceMessage.Level) | |||||
{ | { | ||||
var backupColor = Console.ForegroundColor; | |||||
switch (e.TraceMessage.Level) | |||||
{ | |||||
case MqttNetLogLevel.Error: | |||||
Console.ForegroundColor = ConsoleColor.Red; | |||||
break; | |||||
case MqttNetLogLevel.Warning: | |||||
Console.ForegroundColor = ConsoleColor.Yellow; | |||||
break; | |||||
case MqttNetLogLevel.Info: | |||||
Console.ForegroundColor = ConsoleColor.Green; | |||||
break; | |||||
case MqttNetLogLevel.Verbose: | |||||
Console.ForegroundColor = ConsoleColor.Gray; | |||||
break; | |||||
} | |||||
Console.Write(output); | |||||
Console.ForegroundColor = backupColor; | |||||
case MqttNetLogLevel.Error: | |||||
color = ConsoleColor.Red; | |||||
break; | |||||
case MqttNetLogLevel.Warning: | |||||
color = ConsoleColor.Yellow; | |||||
break; | |||||
case MqttNetLogLevel.Info: | |||||
color = ConsoleColor.Green; | |||||
break; | |||||
case MqttNetLogLevel.Verbose: | |||||
color = ConsoleColor.Gray; | |||||
break; | |||||
} | } | ||||
PrintToConsole(output.ToString(), color); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,9 +1,9 @@ | |||||
using System; | using System; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using MQTTnet.Diagnostics; | |||||
using MQTTnet.Protocol; | using MQTTnet.Protocol; | ||||
using MQTTnet.Server; | using MQTTnet.Server; | ||||
using Newtonsoft.Json.Linq; | |||||
namespace MQTTnet.TestApp.NetCore | namespace MQTTnet.TestApp.NetCore | ||||
{ | { | ||||
@@ -63,6 +63,25 @@ namespace MQTTnet.TestApp.NetCore | |||||
//options.TlsEndpointOptions.IsEnabled = false; | //options.TlsEndpointOptions.IsEnabled = false; | ||||
var mqttServer = new MqttFactory().CreateMqttServer(); | var mqttServer = new MqttFactory().CreateMqttServer(); | ||||
mqttServer.ApplicationMessageReceived += (s, e) => | |||||
{ | |||||
MqttNetConsoleLogger.PrintToConsole( | |||||
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}'", | |||||
ConsoleColor.Magenta); | |||||
}; | |||||
options.ApplicationMessageInterceptor = c => | |||||
{ | |||||
var content = JObject.Parse(Encoding.UTF8.GetString(c.ApplicationMessage.Payload)); | |||||
var timestampProperty = content.Property("timestamp"); | |||||
if (timestampProperty != null && timestampProperty.Value.Type == JTokenType.Null) | |||||
{ | |||||
timestampProperty.Value = DateTime.Now.ToString("O"); | |||||
c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString()); | |||||
} | |||||
}; | |||||
mqttServer.ClientDisconnected += (s, e) => | mqttServer.ClientDisconnected += (s, e) => | ||||
{ | { | ||||
Console.Write("Client disconnected event fired."); | Console.Write("Client disconnected event fired."); | ||||
@@ -407,12 +407,14 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
{ | { | ||||
// Configure MQTT server. | // Configure MQTT server. | ||||
var optionsBuilder = new MqttServerOptionsBuilder() | |||||
.WithConnectionBacklog(100) | |||||
.WithDefaultEndpointPort(1884); | |||||
var options = new MqttServerOptions | var options = new MqttServerOptions | ||||
{ | { | ||||
ConnectionBacklog = 100 | |||||
}; | }; | ||||
options.DefaultEndpointOptions.Port = 1884; | |||||
options.ConnectionValidator = c => | options.ConnectionValidator = c => | ||||
{ | { | ||||
if (c.ClientId != "Highlander") | if (c.ClientId != "Highlander") | ||||
@@ -425,7 +427,7 @@ namespace MQTTnet.TestApp.UniversalWindows | |||||
}; | }; | ||||
var mqttServer = new MqttFactory().CreateMqttServer(); | var mqttServer = new MqttFactory().CreateMqttServer(); | ||||
await mqttServer.StartAsync(options); | |||||
await mqttServer.StartAsync(optionsBuilder.Build()); | |||||
} | } | ||||
{ | { | ||||