diff --git a/.editorconfig b/.editorconfig index 4b09c85d..bf2e42f8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -186,6 +186,15 @@ csharp_style_unused_value_expression_statement_preference = discard_variable:sug dotnet_diagnostic.IDE0058.severity = suggestion csharp_style_unused_value_assignment_preference = discard_variable:suggestion dotnet_diagnostic.IDE0059.severity = suggestion +dotnet_diagnostic.IDE0002.severity = none +dotnet_diagnostic.IDE0010.severity = none +dotnet_diagnostic.IDE0021.severity = none +dotnet_diagnostic.IDE0028.severity = none +dotnet_diagnostic.IDE0049.severity = none +dotnet_diagnostic.IDE0053.severity = none +dotnet_diagnostic.IDE0090.severity = none +dotnet_diagnostic.IDE0290.severity = none +dotnet_diagnostic.SA1508.severity = none ########################################## # Formatting Rules diff --git a/.vscode/settings.json b/.vscode/settings.json index feb9c5c3..5fea68f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,8 @@ ], "cSpell.words": [ "CONNACK", + "hivemq", + "hivemqtt", "PUBACK", "PUBCOMP", "PUBREC", diff --git a/Examples/HiveMQtt-CLI/HiveMQtt-CLI/HiveMQtt-CLI.csproj b/Examples/HiveMQtt-CLI/HiveMQtt-CLI/HiveMQtt-CLI.csproj index ad428fd3..48ab58d9 100644 --- a/Examples/HiveMQtt-CLI/HiveMQtt-CLI/HiveMQtt-CLI.csproj +++ b/Examples/HiveMQtt-CLI/HiveMQtt-CLI/HiveMQtt-CLI.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net6.0 HiveMQtt_CLI enable enable diff --git a/Examples/Reconnect/.vscode/launch.json b/Examples/Reconnect/.vscode/launch.json index e1dfc16d..02b00af0 100644 --- a/Examples/Reconnect/.vscode/launch.json +++ b/Examples/Reconnect/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/bin/Debug/net7.0/Reconnect.dll", + "program": "${workspaceFolder}/bin/Debug/net6.0/Reconnect.dll", "args": [], "cwd": "${workspaceFolder}", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console diff --git a/Examples/Reconnect/NLog.config b/Examples/Reconnect/NLog.config new file mode 100644 index 00000000..545f1e8a --- /dev/null +++ b/Examples/Reconnect/NLog.config @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Reconnect/Program.cs b/Examples/Reconnect/Program.cs index 4d9d38ec..828b218c 100644 --- a/Examples/Reconnect/Program.cs +++ b/Examples/Reconnect/Program.cs @@ -1,12 +1,16 @@ using HiveMQtt.Client; +using HiveMQtt.Client.Exceptions; using HiveMQtt.Client.Options; using System.Text.Json; +using System.Diagnostics; var topic = "hivemqtt/waiting/game"; -var options = new HiveMQClientOptions(); -options.Host = "127.0.0.1"; -options.Port = 1883; +var options = new HiveMQClientOptions +{ + Host = "127.0.0.1", + Port = 1883, +}; var client = new HiveMQClient(options); @@ -74,12 +78,12 @@ break; } } - catch (Exception ex) + catch (HiveMQttClientException ex) { - Console.WriteLine($"--> Failed to connect: {ex.Message}"); + Console.WriteLine($"--> Failed to reconnect: {ex.Message}"); // Double the delay with each failed retry to a maximum - delay = Math.Min(delay * 2, 10000); + delay = Math.Min(delay * 2, 15000); Console.WriteLine($"--> Will delay for {delay / 1000} seconds until next try."); } } @@ -123,7 +127,6 @@ }) ).ConfigureAwait(false); - while (true) { await Task.Delay(2000).ConfigureAwait(false); diff --git a/Examples/Reconnect/Reconnect.csproj b/Examples/Reconnect/Reconnect.csproj index 0e7e705f..6a80f800 100644 --- a/Examples/Reconnect/Reconnect.csproj +++ b/Examples/Reconnect/Reconnect.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net6.0 enable enable @@ -13,8 +13,16 @@ + + 4 + - + + + + PreserveNewest + + diff --git a/Source/HiveMQtt/Client/HiveMQClient.cs b/Source/HiveMQtt/Client/HiveMQClient.cs index 9855980e..ed4f0407 100644 --- a/Source/HiveMQtt/Client/HiveMQClient.cs +++ b/Source/HiveMQtt/Client/HiveMQClient.cs @@ -42,6 +42,7 @@ public HiveMQClient(HiveMQClientOptions? options = null) { options ??= new HiveMQClientOptions(); this.Options = options; + this.cancellationSource = new CancellationTokenSource(); } /// diff --git a/Source/HiveMQtt/Client/HiveMQClientEvents.cs b/Source/HiveMQtt/Client/HiveMQClientEvents.cs index ed7c5bc8..5a8b7847 100644 --- a/Source/HiveMQtt/Client/HiveMQClientEvents.cs +++ b/Source/HiveMQtt/Client/HiveMQClientEvents.cs @@ -16,7 +16,6 @@ namespace HiveMQtt.Client; using System; -using System.Diagnostics; using HiveMQtt.Client.Events; using HiveMQtt.Client.Options; using HiveMQtt.Client.Results; diff --git a/Source/HiveMQtt/Client/HiveMQClientSocket.cs b/Source/HiveMQtt/Client/HiveMQClientSocket.cs index d3ec55df..4ef2d348 100644 --- a/Source/HiveMQtt/Client/HiveMQClientSocket.cs +++ b/Source/HiveMQtt/Client/HiveMQClientSocket.cs @@ -29,11 +29,11 @@ namespace HiveMQtt.Client; /// public partial class HiveMQClient : IDisposable, IHiveMQClient { + private readonly CancellationTokenSource cancellationSource; private Socket? socket; private Stream? stream; private PipeReader? reader; private PipeWriter? writer; - private CancellationTokenSource cancellationSource; private CancellationToken outFlowCancellationToken; private CancellationToken infoFlowCancellationToken; @@ -131,7 +131,6 @@ internal async Task ConnectSocketAsync() this.writer = PipeWriter.Create(this.stream); // Setup the cancellation tokens - this.cancellationSource = new CancellationTokenSource(); this.outFlowCancellationToken = this.cancellationSource.Token; this.infoFlowCancellationToken = this.cancellationSource.Token; diff --git a/Source/HiveMQtt/Client/HiveMQClientTrafficProcessor.cs b/Source/HiveMQtt/Client/HiveMQClientTrafficProcessor.cs index c985db44..f8f72def 100644 --- a/Source/HiveMQtt/Client/HiveMQClientTrafficProcessor.cs +++ b/Source/HiveMQtt/Client/HiveMQClientTrafficProcessor.cs @@ -207,7 +207,7 @@ private Task TrafficInflowProcessorAsync(CancellationToken cancellationTok if (readResult.IsCompleted) { // This is an unexpected exit and may be due to a network failure. - Logger.Trace("TrafficInflowProcessor IsCompleted: end of the streamx"); + Logger.Trace("TrafficInflowProcessor IsCompleted: end of the stream"); if (this.connectState == ConnectState.Connected) { @@ -220,6 +220,7 @@ private Task TrafficInflowProcessorAsync(CancellationToken cancellationTok this.cancellationSource.Cancel(); return false; } + return true; } @@ -308,7 +309,7 @@ private Task TrafficInflowProcessorAsync(CancellationToken cancellationTok /// /// Handle an incoming Publish packet. /// - /// + /// The received publish packet. internal void HandleIncomingPublishPacket(PublishPacket publishPacket) { Logger.Trace("<-- Publish"); @@ -339,8 +340,8 @@ internal void HandleIncomingPublishPacket(PublishPacket publishPacket) /// /// Handle an incoming ConnAck packet. /// - /// - /// + /// The received PubAck packet. + /// Raised if the packet identifier is unknown. internal void HandleIncomingPubAckPacket(PubAckPacket pubAckPacket) { Logger.Trace("<-- PubAck"); @@ -362,7 +363,7 @@ internal void HandleIncomingPubAckPacket(PubAckPacket pubAckPacket) /// /// Handle an incoming PubRec packet. /// - /// + /// The received PubRec packet. internal void HandleIncomingPubRecPacket(PubRecPacket pubRecPacket) { Logger.Trace("<-- PubRec"); @@ -394,7 +395,7 @@ internal void HandleIncomingPubRecPacket(PubRecPacket pubRecPacket) /// /// Handle an incoming PubRel packet. /// - /// + /// The received PubRel packet. internal void HandleIncomingPubRelPacket(PubRelPacket pubRelPacket) { Logger.Trace("<-- PubRel"); @@ -417,8 +418,8 @@ internal void HandleIncomingPubRelPacket(PubRelPacket pubRelPacket) /// /// Handle an incoming PubComp packet. /// - /// - /// + /// The received PubComp packet. + /// Raised if the packet identifier is unknown. internal void HandleIncomingPubCompPacket(PubCompPacket pubCompPacket) { Logger.Trace("<-- PubComp"); @@ -433,10 +434,10 @@ internal void HandleIncomingPubCompPacket(PubCompPacket pubCompPacket) /// /// Write a buffer to the stream. /// - /// - /// - /// - /// + /// The buffer to write. + /// The cancellation token. + /// A FlushResult wrapped in a ValueTask. + /// Raised if the writer is null. internal ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default) { if (this.writer is null) @@ -450,9 +451,9 @@ internal ValueTask WriteAsync(ReadOnlyMemory source, Cancella /// /// Read a buffer from the stream. /// - /// - /// - /// + /// The cancellation token. + /// A ReadResult wrapped in a ValueTask. + /// Raised if the reader is null. internal ValueTask ReadAsync(CancellationToken cancellationToken = default) { if (this.reader is null) diff --git a/Source/HiveMQtt/Client/HiveMQClientUtil.cs b/Source/HiveMQtt/Client/HiveMQClientUtil.cs index 6cf1ac9f..74882396 100644 --- a/Source/HiveMQtt/Client/HiveMQClientUtil.cs +++ b/Source/HiveMQtt/Client/HiveMQClientUtil.cs @@ -18,8 +18,8 @@ namespace HiveMQtt.Client; /// public partial class HiveMQClient : IDisposable, IHiveMQClient { - private bool disposed = false; - private int lastPacketId = 0; + private bool disposed; + private int lastPacketId; /// /// https://learn.microsoft.com/en-us/dotnet/api/system.idisposable?view=net-6.0. diff --git a/Source/HiveMQtt/Client/Results/UnsubscribeResult.cs b/Source/HiveMQtt/Client/Results/UnsubscribeResult.cs index 78800096..6f6e7208 100644 --- a/Source/HiveMQtt/Client/Results/UnsubscribeResult.cs +++ b/Source/HiveMQtt/Client/Results/UnsubscribeResult.cs @@ -19,7 +19,6 @@ namespace HiveMQtt.Client.Results; public class UnsubscribeResult { - // FIXME: List of subscriptions with their unsubscribe reason codes public UnsubscribeResult() { this.Subscriptions = new List(); diff --git a/Source/HiveMQtt/HiveMQtt.sln b/Source/HiveMQtt/HiveMQtt.sln index b2dd6cf5..b8545607 100644 --- a/Source/HiveMQtt/HiveMQtt.sln +++ b/Source/HiveMQtt/HiveMQtt.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiveMQtt", "HiveMQtt.csproj EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiveMQtt-CLI", "..\..\Examples\HiveMQtt-CLI\HiveMQtt-CLI\HiveMQtt-CLI.csproj", "{B7404198-178C-43C5-9136-4BF25D23EC7E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reconnect", "..\..\Examples\Reconnect\Reconnect.csproj", "{FE0AD218-169C-4DE6-ADBE-0B55695620B4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {B7404198-178C-43C5-9136-4BF25D23EC7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7404198-178C-43C5-9136-4BF25D23EC7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7404198-178C-43C5-9136-4BF25D23EC7E}.Release|Any CPU.Build.0 = Release|Any CPU + {FE0AD218-169C-4DE6-ADBE-0B55695620B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE0AD218-169C-4DE6-ADBE-0B55695620B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE0AD218-169C-4DE6-ADBE-0B55695620B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE0AD218-169C-4DE6-ADBE-0B55695620B4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/HiveMQtt/Properties/AssemblyInfo.cs b/Source/HiveMQtt/Properties/AssemblyInfo.cs index e60eeec6..58758516 100644 --- a/Source/HiveMQtt/Properties/AssemblyInfo.cs +++ b/Source/HiveMQtt/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ using System.Runtime.CompilerServices; -[assembly:InternalsVisibleTo("HiveMQtt.Test")] +[assembly: InternalsVisibleTo("HiveMQtt.Test")] [assembly: CLSCompliant(true)] diff --git a/Tests/HiveMQtt.Test/HiveMQClient/HiveMQClientConnectTest.cs b/Tests/HiveMQtt.Test/HiveMQClient/HiveMQClientConnectTest.cs index 3efb0538..0eb2c755 100644 --- a/Tests/HiveMQtt.Test/HiveMQClient/HiveMQClientConnectTest.cs +++ b/Tests/HiveMQtt.Test/HiveMQClient/HiveMQClientConnectTest.cs @@ -1,5 +1,6 @@ namespace HiveMQtt.Test.HiveMQClient; +using System.Globalization; using System.Threading.Tasks; using HiveMQtt.Client; using HiveMQtt.Client.Events; @@ -161,7 +162,7 @@ public async Task Test_AfterDisconnectEvent_Async() // Assert that all Events were called Assert.True(client.LocalStore.ContainsKey("AfterDisconnectHandlerCalled")); Assert.True(client.LocalStore.ContainsKey("AfterDisconnectHandlerCalledCount")); - Assert.Equal(client.LocalStore["AfterDisconnectHandlerCalledCount"], "1"); + Assert.Equal("1", client.LocalStore["AfterDisconnectHandlerCalledCount"]); // Remove event handlers client.AfterDisconnect -= AfterDisconnectHandler; @@ -232,11 +233,11 @@ private static void AfterDisconnectHandler(object? sender, AfterDisconnectEventA { var client = (HiveMQClient)sender; - if (client.LocalStore.ContainsKey("AfterDisconnectHandlerCalled")) + if (client.LocalStore.TryGetValue("AfterDisconnectHandlerCalled", out var value)) { - var count = Convert.ToInt16(client.LocalStore["AfterDisconnectHandlerCalledCount"]); + var count = Convert.ToInt16(value, CultureInfo.InvariantCulture); count++; - client.LocalStore.Add("AfterDisconnectHandlerCalledCount", count.ToString()); + client.LocalStore.Add("AfterDisconnectHandlerCalledCount", count.ToString(CultureInfo.InvariantCulture)); } else { @@ -253,11 +254,11 @@ private static void OnDisconnectSentHandler(object? sender, OnDisconnectSentEven { var client = (HiveMQClient)sender; - if (client.LocalStore.ContainsKey("OnDisconnectSentHandlerCalled")) + if (client.LocalStore.TryGetValue("OnDisconnectSentHandlerCalled", out var value)) { - var count = Convert.ToInt16(client.LocalStore["OnDisconnectSentHandlerCalledCount"]); + var count = Convert.ToInt16(value, CultureInfo.InvariantCulture); count++; - client.LocalStore.Add("OnDisconnectSentHandlerCalledCount", count.ToString()); + client.LocalStore.Add("OnDisconnectSentHandlerCalledCount", count.ToString(CultureInfo.InvariantCulture)); } else {