/// <inheritdoc cref="IMqttClientConfigBuilder.AddUserProperty" /> public IMqttClientConfigBuilder AddUserProperty(string name, string?value) { Check.NotNull(name, nameof(name)); _builder.WithUserProperty(name, value); return(this); }
public async Task ConnectAsync(Options options, TaskCompletionSource <int> closedPromise) { if (options.Verbose) { MqttNetGlobalLogger.LogMessagePublished += (s, e) => { var trace = $">> [{e.LogMessage.Timestamp:O}] [{e.LogMessage.ThreadId}] [{e.LogMessage.Source}] [{e.LogMessage.Level}]: {e.LogMessage.Message}"; if (e.LogMessage.Exception != null) { trace += Environment.NewLine + e.LogMessage.Exception.ToString(); } Console.WriteLine(trace); }; } var factory = new MqttFactory(); var client = factory.CreateMqttClient(); client.UseApplicationMessageReceivedHandler(msg => this.HandleMessageAsync(msg)); var tlsOptions = new MqttClientOptionsBuilderTlsParameters(); tlsOptions.UseTls = true; var clientOptions = new MqttClientOptionsBuilder() .WithTcpServer(opt => opt.NoDelay = true) .WithClientId(options.ClientId) .WithTcpServer(options.Hostname, 8883) .WithTls(tlsOptions) .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500) // .WithUserProperty("host", hostName) // normally it is not needed as SNI is added by most TLS implementations. .WithUserProperty("api-version", "2020-10-01-preview") .WithCommunicationTimeout(TimeSpan.FromSeconds(30)) .WithKeepAlivePeriod(TimeSpan.FromSeconds(300)) .WithCleanSession(false); // keep existing subscriptions if (string.IsNullOrEmpty(options.SasKey)) { tlsOptions.Certificates = new[] { new X509Certificate(options.ClientCertificatePath, options.ClientCertificatePassword) }; clientOptions.WithAuthentication("X509", null); } else { var at = DateTimeOffset.UtcNow; var atString = at.ToUnixTimeMilliseconds().ToString(); var expiry = at.AddMinutes(40); var expiryString = expiry.ToUnixTimeMilliseconds().ToString(); string toSign = $"{options.Hostname}\n{options.ClientId}\n{options.SasPolicy}\n{atString}\n{expiryString}\n"; var hmac = new HMACSHA256(Convert.FromBase64String(options.SasKey)); var sas = hmac.ComputeHash(Encoding.UTF8.GetBytes(toSign)); clientOptions .WithAuthentication("SAS", sas) .WithUserProperty("sas-at", atString) .WithUserProperty("sas-expiry", expiryString); if (!string.IsNullOrEmpty(options.SasPolicy)) { // include only if using SAS policy clientOptions.WithUserProperty("sas-policy", options.SasPolicy); } } // Set up disconnection handling: print out details and allow process to close client.UseDisconnectedHandler(disconnectArgs => { Console.WriteLine($"Disconnected: {disconnectArgs.ReasonCode}"); if (disconnectArgs.AuthenticateResult?.UserProperties != null) { foreach (var prop in disconnectArgs.AuthenticateResult.UserProperties) { Console.WriteLine($"{prop.Name}: {prop.Value}"); } } closedPromise.SetResult(1); }); try { // once connection is established, we may start receiving messages based on subscriptions // from previous connections - better set client before connection. this.client = client; var connectResult = await client.ConnectAsync(clientOptions.Build(), CancellationToken.None); if (connectResult.ResultCode != MqttClientConnectResultCode.Success) { var status = GetStatus(connectResult.UserProperties)?.ToString("x4"); throw new Exception($"Connect failed. Status: {connectResult.ResultCode}; status: {status}"); } if (!connectResult.IsSessionPresent) { // only subscribe if haven't subscribed already. // This optimization only works of a single SUBSCRIBE is used to subscribe to everything at once or // if app keeps track of what has been successfully acknowledged by server. var subscribeResult = await client.SubscribeAsync( new MqttTopicFilter { Topic = "$iothub/methods/+", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }, new MqttTopicFilter { Topic = "$iothub/commands", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }, new MqttTopicFilter { Topic = "$iothub/twin/patch/desired", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); // make sure subscriptions were successful if (subscribeResult.Items.Count != 3 || subscribeResult.Items[0].ResultCode != MqttClientSubscribeResultCode.GrantedQoS0 || subscribeResult.Items[1].ResultCode != MqttClientSubscribeResultCode.GrantedQoS1 || subscribeResult.Items[2].ResultCode != MqttClientSubscribeResultCode.GrantedQoS0) { throw new ApplicationException("Failed to subscribe"); } } } catch (MqttConnectingFailedException ex) { Console.WriteLine($"Failed to connect, reason code: {ex.ResultCode}"); if (ex.Result?.UserProperties != null) { foreach (var prop in ex.Result.UserProperties) { Console.WriteLine($"{prop.Name}: {prop.Value}"); } } throw; } }