dotnet / MQTTnet

MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
MIT License
4.41k stars 1.06k forks source link

Sometime the client can't connecte to server even if the MqttConnectionValidate method finished #1901

Open 952795 opened 9 months ago

952795 commented 9 months ago

Describe your question

I tested the client(using the MQTTX) and the server revently. The client can't connecte the server without any error sometimes, and When I test the Broker with breakpoint, I found that the MqttConnectionValidate method have finished and execute the code 'c.ReasonCode = MqttConnectReasonCode.Success;' in fact. I'm not sure the probem is a bug or a usage mistake.

Which project is your question related to?

Part of My Code

code

chkr1011 commented 8 months ago

What error do you get on client side if the connection was not successful?

952795 commented 8 months ago

Thanks for your attention very much. The Client didn't show any error message, it just waited for the connectino until timeout. I have checked the code again recently, and I found that when I use the managedClient, the 'dispose' method seems run incorrectly(run the dispose before 'managedMqttClient.InternalClient.DisconnectAsync()', but sometimes the InternalClient is 'null'). Then I added a try-catch there, this problem seems to be fixed. But, I'm still not sure if this causes the problem.

952795 commented 8 months ago

the Dispose() before I added try-catch is like this: await managedMqttClient.InternalClient.DisconnectAsync(); managedMqttClient.Dispose(); If the 'managedMqttClient.Dispose()' dosen't run, Does this mean the half-connection? And many half-connections cause the problem.

chkr1011 commented 8 months ago

Please share the code which uses the managed client. In general I recommend to not actively work with the internal client. It is (or should) be maintained properly internally by the managed client.

952795 commented 8 months ago

Thanks, I will pay attention to that. Below is the code about the managedClient. I use it to publish some mesage in a web-api controller. `using iGeoTek.Contracts; using MQTTnet; using MQTTnet.Client; using MQTTnet.Extensions.ManagedClient; using System; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks;

namespace iGeoTek.MQTTExtension { ///

/// MQTT消息Publish服务 /// public class MQTTClient : IDisposable { private static readonly AppSettings appSettings = new AppSettings(); private ManagedMqttClientOptions managedMqttClientOptions { set; get; } public IManagedMqttClient managedMqttClient { set; get; }

    public MQTTClient(X509Certificate2 x509Certificate2)
    {
        var mqttClientOptions = new MqttClientOptionsBuilder()
            .WithTlsOptions(new MqttClientTlsOptionsBuilder()
               .WithClientCertificates(new List<X509Certificate2>() { x509Certificate2 })
               .WithCertificateValidationHandler((certContext) =>
               {
                   using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
                   {
                       try
                       {
                           store.Open(OpenFlags.ReadOnly);
                           var caCerts = store.Certificates.Find(X509FindType.FindByThumbprint, appSettings.MQTTService.CAX509Thumbprint, false);
                           if (caCerts.Count == 0)
                           {
                               Console.WriteLine("CA certificate is not found.");
                               return false;
                           }
                           var caCert = caCerts[0];
                           certContext.Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                           certContext.Chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
                           certContext.Chain.ChainPolicy.ExtraStore.Add(caCert);
                           bool res = certContext.Chain.Build(new X509Certificate2(certContext.Certificate));
                           return res;
                       }
                       catch (Exception error)
                       {
                           throw error;
                       }
                   }
               })
               .Build())
            .Build();

        managedMqttClientOptions = new ManagedMqttClientOptionsBuilder()
            .WithClientOptions(mqttClientOptions)
            .Build();

    }

    public async void Connect()
    {
        var mqttFactory = new MqttFactory();
        managedMqttClient = mqttFactory.CreateManagedMqttClient();
        await managedMqttClient.StartAsync(managedMqttClientOptions);
        //wait for connection
        using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(20)))
        {
            while (!timeoutToken.Token.IsCancellationRequested)
            {
                if (managedMqttClient.IsConnected) return;
            }
            this.Dispose();
            throw new Exception("Client connected fail.");
        }
    }

    public async Task<bool> Publish(string responsePaylod, string topic)
    {
        var applicationMessage = new MqttApplicationMessageBuilder()
            .WithTopic(topic)
            .WithPayload(responsePaylod)
            .Build();
        var result = await managedMqttClient.InternalClient.PublishAsync(applicationMessage, CancellationToken.None);
        return result.IsSuccess;
    }

    public async void Dispose()
    {
        //before add try-catch
        await managedMqttClient.InternalClient.DisconnectAsync();
        managedMqttClient.Dispose();

        //add try-catch
        /*try
        {
            await managedMqttClient.InternalClient.DisconnectAsync();
        }
        catch { }
        finally
        {
            managedMqttClient.Dispose();
        }  */         
    }
}

}`

In addtion, There is another change I forgot last time. I find the wrong X509 CA File will get a 'true' result from the 'certContext.Chain.Build()', then I change the code of 'WithCertificateValidationHandler' to make sure the X509 CA file is the true file in the MQTTClient above and MQTTSerice. Finally, with this change, the program dosen't show the problem of connection in the 6 days's testing.