You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

704 lines
24 KiB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.ObjectModel;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Windows.Security.Cryptography.Certificates;
  7. using Windows.UI.Core;
  8. using Windows.UI.Xaml;
  9. using MQTTnet.Client;
  10. using MQTTnet.Client.Options;
  11. using MQTTnet.Diagnostics;
  12. using MQTTnet.Exceptions;
  13. using MQTTnet.Extensions.ManagedClient;
  14. using MQTTnet.Extensions.Rpc;
  15. using MQTTnet.Formatter;
  16. using MQTTnet.Implementations;
  17. using MQTTnet.Protocol;
  18. using MQTTnet.Server;
  19. using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
  20. using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
  21. namespace MQTTnet.TestApp.UniversalWindows
  22. {
  23. public sealed partial class MainPage
  24. {
  25. private readonly ConcurrentQueue<MqttNetLogMessage> _traceMessages = new ConcurrentQueue<MqttNetLogMessage>();
  26. private readonly ObservableCollection<IMqttClientSessionStatus> _sessions = new ObservableCollection<IMqttClientSessionStatus>();
  27. private IMqttClient _mqttClient;
  28. private IManagedMqttClient _managedMqttClient;
  29. private IMqttServer _mqttServer;
  30. public MainPage()
  31. {
  32. InitializeComponent();
  33. ClientId.Text = Guid.NewGuid().ToString("D");
  34. MqttNetGlobalLogger.LogMessagePublished += OnTraceMessagePublished;
  35. }
  36. private async void OnTraceMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e)
  37. {
  38. _traceMessages.Enqueue(e.TraceMessage);
  39. await UpdateLogAsync();
  40. }
  41. private async Task UpdateLogAsync()
  42. {
  43. while (_traceMessages.Count > 100)
  44. {
  45. _traceMessages.TryDequeue(out _);
  46. }
  47. var logText = new StringBuilder();
  48. foreach (var traceMessage in _traceMessages)
  49. {
  50. logText.AppendFormat(
  51. "[{0:yyyy-MM-dd HH:mm:ss.fff}] [{1}] [{2}] [{3}] [{4}]{5}",
  52. traceMessage.Timestamp,
  53. traceMessage.Level,
  54. traceMessage.Source,
  55. traceMessage.ThreadId,
  56. traceMessage.Message,
  57. Environment.NewLine);
  58. if (traceMessage.Exception != null)
  59. {
  60. logText.AppendLine(traceMessage.Exception.ToString());
  61. }
  62. }
  63. await Trace.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
  64. {
  65. Trace.Text = logText.ToString();
  66. });
  67. }
  68. private async void Connect(object sender, RoutedEventArgs e)
  69. {
  70. var tlsOptions = new MqttClientTlsOptions
  71. {
  72. UseTls = UseTls.IsChecked == true,
  73. IgnoreCertificateChainErrors = true,
  74. IgnoreCertificateRevocationErrors = true,
  75. AllowUntrustedCertificates = true
  76. };
  77. var options = new MqttClientOptions
  78. {
  79. ClientId = ClientId.Text,
  80. ProtocolVersion = MqttProtocolVersion.V500
  81. };
  82. if (UseTcp.IsChecked == true)
  83. {
  84. options.ChannelOptions = new MqttClientTcpOptions
  85. {
  86. Server = Server.Text,
  87. Port = int.Parse(Port.Text),
  88. TlsOptions = tlsOptions
  89. };
  90. }
  91. if (UseWs.IsChecked == true)
  92. {
  93. options.ChannelOptions = new MqttClientWebSocketOptions
  94. {
  95. Uri = Server.Text,
  96. TlsOptions = tlsOptions
  97. };
  98. }
  99. if (options.ChannelOptions == null)
  100. {
  101. throw new InvalidOperationException();
  102. }
  103. options.Credentials = new MqttClientCredentials
  104. {
  105. Username = User.Text,
  106. Password = Password.Text
  107. };
  108. options.CleanSession = CleanSession.IsChecked == true;
  109. options.KeepAlivePeriod = TimeSpan.FromSeconds(double.Parse(KeepAliveInterval.Text));
  110. if (UseMqtt310.IsChecked == true)
  111. {
  112. options.ProtocolVersion = MqttProtocolVersion.V310;
  113. }
  114. else if (UseMqtt311.IsChecked == true)
  115. {
  116. options.ProtocolVersion = MqttProtocolVersion.V311;
  117. }
  118. else if (UseMqtt500.IsChecked == true)
  119. {
  120. options.ProtocolVersion = MqttProtocolVersion.V500;
  121. }
  122. try
  123. {
  124. if (_mqttClient != null)
  125. {
  126. await _mqttClient.DisconnectAsync();
  127. _mqttClient.ApplicationMessageReceived -= OnApplicationMessageReceived;
  128. _mqttClient.Connected -= OnConnected;
  129. _mqttClient.Disconnected -= OnDisconnected;
  130. }
  131. var factory = new MqttFactory();
  132. if (UseManagedClient.IsChecked == true)
  133. {
  134. _managedMqttClient = factory.CreateManagedMqttClient();
  135. _managedMqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
  136. _managedMqttClient.Connected += OnConnected;
  137. _managedMqttClient.Disconnected += OnDisconnected;
  138. await _managedMqttClient.StartAsync(new ManagedMqttClientOptions
  139. {
  140. ClientOptions = options
  141. });
  142. }
  143. else
  144. {
  145. _mqttClient = factory.CreateMqttClient();
  146. _mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
  147. _mqttClient.Connected += OnConnected;
  148. _mqttClient.Disconnected += OnDisconnected;
  149. await _mqttClient.ConnectAsync(options);
  150. }
  151. }
  152. catch (Exception exception)
  153. {
  154. Trace.Text += exception + Environment.NewLine;
  155. }
  156. }
  157. private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs e)
  158. {
  159. _traceMessages.Enqueue(new MqttNetLogMessage("", DateTime.Now, -1,
  160. "", MqttNetLogLevel.Info, "! DISCONNECTED EVENT FIRED", null));
  161. Task.Run(UpdateLogAsync);
  162. }
  163. private void OnConnected(object sender, MqttClientConnectedEventArgs e)
  164. {
  165. _traceMessages.Enqueue(new MqttNetLogMessage("", DateTime.Now, -1,
  166. "", MqttNetLogLevel.Info, "! CONNECTED EVENT FIRED", null));
  167. Task.Run(UpdateLogAsync);
  168. }
  169. private async void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
  170. {
  171. var item = $"Timestamp: {DateTime.Now:O} | Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload)} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel}";
  172. await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
  173. {
  174. if (AddReceivedMessagesToList.IsChecked == true)
  175. {
  176. ReceivedMessages.Items.Add(item);
  177. }
  178. });
  179. }
  180. private async void Publish(object sender, RoutedEventArgs e)
  181. {
  182. try
  183. {
  184. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  185. if (QoS1.IsChecked == true)
  186. {
  187. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  188. }
  189. if (QoS2.IsChecked == true)
  190. {
  191. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  192. }
  193. var payload = new byte[0];
  194. if (PlainText.IsChecked == true)
  195. {
  196. payload = Encoding.UTF8.GetBytes(Payload.Text);
  197. }
  198. if (Base64.IsChecked == true)
  199. {
  200. payload = Convert.FromBase64String(Payload.Text);
  201. }
  202. var message = new MqttApplicationMessageBuilder()
  203. .WithContentType(ContentType.Text)
  204. .WithResponseTopic(ResponseTopic.Text)
  205. .WithTopic(Topic.Text)
  206. .WithPayload(payload)
  207. .WithQualityOfServiceLevel(qos)
  208. .WithRetainFlag(Retain.IsChecked == true)
  209. .Build();
  210. if (_mqttClient != null)
  211. {
  212. await _mqttClient.PublishAsync(message);
  213. }
  214. if (_managedMqttClient != null)
  215. {
  216. await _managedMqttClient.PublishAsync(message);
  217. }
  218. }
  219. catch (Exception exception)
  220. {
  221. Trace.Text += exception + Environment.NewLine;
  222. }
  223. }
  224. private async void Disconnect(object sender, RoutedEventArgs e)
  225. {
  226. try
  227. {
  228. if (_mqttClient != null)
  229. {
  230. await _mqttClient.DisconnectAsync();
  231. _mqttClient.Dispose();
  232. _mqttClient = null;
  233. }
  234. if (_managedMqttClient != null)
  235. {
  236. await _managedMqttClient.StopAsync();
  237. _managedMqttClient.Dispose();
  238. _managedMqttClient = null;
  239. }
  240. }
  241. catch (Exception exception)
  242. {
  243. Trace.Text += exception + Environment.NewLine;
  244. }
  245. }
  246. private void ClearLog(object sender, RoutedEventArgs e)
  247. {
  248. while (_traceMessages.Count > 0)
  249. {
  250. _traceMessages.TryDequeue(out _);
  251. }
  252. Trace.Text = string.Empty;
  253. }
  254. private async void Subscribe(object sender, RoutedEventArgs e)
  255. {
  256. try
  257. {
  258. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  259. if (SubscribeQoS1.IsChecked == true)
  260. {
  261. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  262. }
  263. if (SubscribeQoS2.IsChecked == true)
  264. {
  265. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  266. }
  267. var topicFilter = new TopicFilter { Topic = SubscribeTopic.Text, QualityOfServiceLevel = qos };
  268. if (_mqttClient != null)
  269. {
  270. await _mqttClient.SubscribeAsync(topicFilter);
  271. }
  272. if (_managedMqttClient != null)
  273. {
  274. await _managedMqttClient.SubscribeAsync(topicFilter);
  275. }
  276. }
  277. catch (Exception exception)
  278. {
  279. Trace.Text += exception + Environment.NewLine;
  280. }
  281. }
  282. private async void Unsubscribe(object sender, RoutedEventArgs e)
  283. {
  284. try
  285. {
  286. if (_mqttClient != null)
  287. {
  288. await _mqttClient.UnsubscribeAsync(SubscribeTopic.Text);
  289. }
  290. if (_managedMqttClient != null)
  291. {
  292. await _managedMqttClient.UnsubscribeAsync(SubscribeTopic.Text);
  293. }
  294. }
  295. catch (Exception exception)
  296. {
  297. Trace.Text += exception + Environment.NewLine;
  298. }
  299. }
  300. // This code is for the Wiki at GitHub!
  301. // ReSharper disable once UnusedMember.Local
  302. private async void StartServer(object sender, RoutedEventArgs e)
  303. {
  304. if (_mqttServer != null)
  305. {
  306. return;
  307. }
  308. JsonServerStorage storage = null;
  309. if (ServerPersistRetainedMessages.IsChecked == true)
  310. {
  311. storage = new JsonServerStorage();
  312. if (ServerClearRetainedMessages.IsChecked == true)
  313. {
  314. storage.Clear();
  315. }
  316. }
  317. _mqttServer = new MqttFactory().CreateMqttServer();
  318. var options = new MqttServerOptions();
  319. options.DefaultEndpointOptions.Port = int.Parse(ServerPort.Text);
  320. options.Storage = storage;
  321. options.EnablePersistentSessions = ServerAllowPersistentSessions.IsChecked == true;
  322. await _mqttServer.StartAsync(options);
  323. }
  324. private async void StopServer(object sender, RoutedEventArgs e)
  325. {
  326. if (_mqttServer == null)
  327. {
  328. return;
  329. }
  330. await _mqttServer.StopAsync();
  331. _mqttServer = null;
  332. }
  333. private void ClearReceivedMessages(object sender, RoutedEventArgs e)
  334. {
  335. ReceivedMessages.Items.Clear();
  336. }
  337. private async void ExecuteRpc(object sender, RoutedEventArgs e)
  338. {
  339. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  340. if (RpcQoS1.IsChecked == true)
  341. {
  342. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  343. }
  344. if (RpcQoS2.IsChecked == true)
  345. {
  346. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  347. }
  348. var payload = new byte[0];
  349. if (RpcText.IsChecked == true)
  350. {
  351. payload = Encoding.UTF8.GetBytes(RpcPayload.Text);
  352. }
  353. if (RpcBase64.IsChecked == true)
  354. {
  355. payload = Convert.FromBase64String(RpcPayload.Text);
  356. }
  357. try
  358. {
  359. var rpcClient = new MqttRpcClient(_mqttClient);
  360. var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), RpcMethod.Text, payload, qos);
  361. RpcResponses.Items.Add(RpcMethod.Text + " >>> " + Encoding.UTF8.GetString(response));
  362. }
  363. catch (MqttCommunicationTimedOutException)
  364. {
  365. RpcResponses.Items.Add(RpcMethod.Text + " >>> [TIMEOUT]");
  366. }
  367. catch (Exception exception)
  368. {
  369. RpcResponses.Items.Add(RpcMethod.Text + " >>> [EXCEPTION (" + exception.Message + ")]");
  370. }
  371. }
  372. private void ClearRpcResponses(object sender, RoutedEventArgs e)
  373. {
  374. RpcResponses.Items.Clear();
  375. }
  376. private void ClearSessions(object sender, RoutedEventArgs e)
  377. {
  378. _sessions.Clear();
  379. }
  380. private void RefreshSessions(object sender, RoutedEventArgs e)
  381. {
  382. if (_mqttServer == null)
  383. {
  384. return;
  385. }
  386. var sessions = _mqttServer.GetClientSessionsStatusAsync().GetAwaiter().GetResult();
  387. _sessions.Clear();
  388. foreach (var session in sessions)
  389. {
  390. _sessions.Add(session);
  391. }
  392. ListViewSessions.DataContext = _sessions;
  393. }
  394. #region Wiki Code
  395. private async Task WikiCode()
  396. {
  397. {
  398. // Write all trace messages to the console window.
  399. MqttNetGlobalLogger.LogMessagePublished += (s, e) =>
  400. {
  401. Console.WriteLine($">> [{e.TraceMessage.Timestamp:O}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Source}] [{e.TraceMessage.Level}]: {e.TraceMessage.Message}");
  402. if (e.TraceMessage.Exception != null)
  403. {
  404. Console.WriteLine(e.TraceMessage.Exception);
  405. }
  406. };
  407. }
  408. {
  409. // Use a custom identifier for the trace messages.
  410. var clientOptions = new MqttClientOptionsBuilder()
  411. .Build();
  412. }
  413. {
  414. // Create a new MQTT client.
  415. var factory = new MqttFactory();
  416. var mqttClient = factory.CreateMqttClient();
  417. {
  418. // Create TCP based options using the builder.
  419. var options = new MqttClientOptionsBuilder()
  420. .WithClientId("Client1")
  421. .WithTcpServer("broker.hivemq.com")
  422. .WithCredentials("bud", "%spencer%")
  423. .WithTls()
  424. .WithCleanSession()
  425. .Build();
  426. await mqttClient.ConnectAsync(options);
  427. }
  428. {
  429. // Use TCP connection.
  430. var options = new MqttClientOptionsBuilder()
  431. .WithTcpServer("broker.hivemq.com", 1883) // Port is optional
  432. .Build();
  433. }
  434. {
  435. // Use secure TCP connection.
  436. var options = new MqttClientOptionsBuilder()
  437. .WithTcpServer("broker.hivemq.com")
  438. .WithTls()
  439. .Build();
  440. }
  441. {
  442. // Use WebSocket connection.
  443. var options = new MqttClientOptionsBuilder()
  444. .WithWebSocketServer("broker.hivemq.com:8000/mqtt")
  445. .Build();
  446. await mqttClient.ConnectAsync(options);
  447. }
  448. {
  449. // Create TCP based options manually
  450. var options = new MqttClientOptions
  451. {
  452. ClientId = "Client1",
  453. Credentials = new MqttClientCredentials
  454. {
  455. Username = "bud",
  456. Password = "%spencer%"
  457. },
  458. ChannelOptions = new MqttClientTcpOptions
  459. {
  460. Server = "broker.hivemq.org",
  461. TlsOptions = new MqttClientTlsOptions
  462. {
  463. UseTls = true
  464. }
  465. },
  466. };
  467. }
  468. {
  469. // Subscribe to a topic
  470. await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic("my/topic").Build());
  471. // Unsubscribe from a topic
  472. await mqttClient.UnsubscribeAsync("my/topic");
  473. // Publish an application message
  474. var applicationMessage = new MqttApplicationMessageBuilder()
  475. .WithTopic("A/B/C")
  476. .WithPayload("Hello World")
  477. .WithAtLeastOnceQoS()
  478. .Build();
  479. await mqttClient.PublishAsync(applicationMessage);
  480. }
  481. }
  482. // ----------------------------------
  483. {
  484. var options = new MqttServerOptions();
  485. options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  486. {
  487. if (c.ClientId.Length < 10)
  488. {
  489. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  490. return;
  491. }
  492. if (c.Username != "mySecretUser")
  493. {
  494. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  495. return;
  496. }
  497. if (c.Password != "mySecretPassword")
  498. {
  499. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  500. return;
  501. }
  502. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  503. });
  504. var factory = new MqttFactory();
  505. var mqttServer = factory.CreateMqttServer();
  506. await mqttServer.StartAsync(options);
  507. Console.WriteLine("Press any key to exit.");
  508. Console.ReadLine();
  509. await mqttServer.StopAsync();
  510. }
  511. // ----------------------------------
  512. // For UWP apps:
  513. MqttTcpChannel.CustomIgnorableServerCertificateErrorsResolver = o =>
  514. {
  515. if (o.Server == "server_with_revoked_cert")
  516. {
  517. return new[] { ChainValidationResult.Revoked };
  518. }
  519. return new ChainValidationResult[0];
  520. };
  521. {
  522. // Start a MQTT server.
  523. var mqttServer = new MqttFactory().CreateMqttServer();
  524. await mqttServer.StartAsync(new MqttServerOptions());
  525. Console.WriteLine("Press any key to exit.");
  526. Console.ReadLine();
  527. await mqttServer.StopAsync();
  528. }
  529. {
  530. // Configure MQTT server.
  531. var optionsBuilder = new MqttServerOptionsBuilder()
  532. .WithConnectionBacklog(100)
  533. .WithDefaultEndpointPort(1884);
  534. var options = new MqttServerOptions
  535. {
  536. };
  537. options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  538. {
  539. if (c.ClientId != "Highlander")
  540. {
  541. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  542. return;
  543. }
  544. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  545. });
  546. var mqttServer = new MqttFactory().CreateMqttServer();
  547. await mqttServer.StartAsync(optionsBuilder.Build());
  548. }
  549. {
  550. // Setup client validator.
  551. var options = new MqttServerOptions
  552. {
  553. ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  554. {
  555. if (c.ClientId.Length < 10)
  556. {
  557. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  558. return;
  559. }
  560. if (c.Username != "mySecretUser")
  561. {
  562. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  563. return;
  564. }
  565. if (c.Password != "mySecretPassword")
  566. {
  567. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  568. return;
  569. }
  570. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  571. })
  572. };
  573. }
  574. {
  575. // Create a new MQTT server.
  576. var mqttServer = new MqttFactory().CreateMqttServer();
  577. }
  578. {
  579. // Setup and start a managed MQTT client.
  580. var options = new ManagedMqttClientOptionsBuilder()
  581. .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
  582. .WithClientOptions(new MqttClientOptionsBuilder()
  583. .WithClientId("Client1")
  584. .WithTcpServer("broker.hivemq.com")
  585. .WithTls().Build())
  586. .Build();
  587. var mqttClient = new MqttFactory().CreateManagedMqttClient();
  588. await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic("my/topic").Build());
  589. await mqttClient.StartAsync(options);
  590. }
  591. }
  592. #endregion
  593. }
  594. }