Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

MqttPacketSerializer.cs 22 KiB

pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
pirms 7 gadiem
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. using MQTTnet.Core.Channel;
  6. using MQTTnet.Core.Exceptions;
  7. using MQTTnet.Core.Packets;
  8. using MQTTnet.Core.Protocol;
  9. namespace MQTTnet.Core.Serializer
  10. {
  11. public sealed class MqttPacketSerializer : IMqttPacketSerializer
  12. {
  13. private static byte[] ProtocolVersionV311Name { get; } = Encoding.UTF8.GetBytes("MQTT");
  14. private static byte[] ProtocolVersionV310Name { get; } = Encoding.UTF8.GetBytes("MQIs");
  15. public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
  16. public Task SerializeAsync(MqttBasePacket packet, IMqttCommunicationChannel destination)
  17. {
  18. if (packet == null) throw new ArgumentNullException(nameof(packet));
  19. if (destination == null) throw new ArgumentNullException(nameof(destination));
  20. if (packet is MqttConnectPacket connectPacket)
  21. {
  22. return SerializeAsync(connectPacket, destination);
  23. }
  24. if (packet is MqttConnAckPacket connAckPacket)
  25. {
  26. return SerializeAsync(connAckPacket, destination);
  27. }
  28. if (packet is MqttDisconnectPacket disconnectPacket)
  29. {
  30. return SerializeAsync(disconnectPacket, destination);
  31. }
  32. if (packet is MqttPingReqPacket pingReqPacket)
  33. {
  34. return SerializeAsync(pingReqPacket, destination);
  35. }
  36. if (packet is MqttPingRespPacket pingRespPacket)
  37. {
  38. return SerializeAsync(pingRespPacket, destination);
  39. }
  40. if (packet is MqttPublishPacket publishPacket)
  41. {
  42. return SerializeAsync(publishPacket, destination);
  43. }
  44. if (packet is MqttPubAckPacket pubAckPacket)
  45. {
  46. return SerializeAsync(pubAckPacket, destination);
  47. }
  48. if (packet is MqttPubRecPacket pubRecPacket)
  49. {
  50. return SerializeAsync(pubRecPacket, destination);
  51. }
  52. if (packet is MqttPubRelPacket pubRelPacket)
  53. {
  54. return SerializeAsync(pubRelPacket, destination);
  55. }
  56. if (packet is MqttPubCompPacket pubCompPacket)
  57. {
  58. return SerializeAsync(pubCompPacket, destination);
  59. }
  60. if (packet is MqttSubscribePacket subscribePacket)
  61. {
  62. return SerializeAsync(subscribePacket, destination);
  63. }
  64. if (packet is MqttSubAckPacket subAckPacket)
  65. {
  66. return SerializeAsync(subAckPacket, destination);
  67. }
  68. if (packet is MqttUnsubscribePacket unsubscribePacket)
  69. {
  70. return SerializeAsync(unsubscribePacket, destination);
  71. }
  72. if (packet is MqttUnsubAckPacket unsubAckPacket)
  73. {
  74. return SerializeAsync(unsubAckPacket, destination);
  75. }
  76. throw new MqttProtocolViolationException("Packet type invalid.");
  77. }
  78. public async Task<MqttBasePacket> DeserializeAsync(IMqttCommunicationChannel source)
  79. {
  80. if (source == null) throw new ArgumentNullException(nameof(source));
  81. using (var mqttPacketReader = new MqttPacketReader(source))
  82. {
  83. await mqttPacketReader.ReadToEndAsync();
  84. switch (mqttPacketReader.ControlPacketType)
  85. {
  86. case MqttControlPacketType.Connect:
  87. {
  88. return await DeserializeConnectAsync(mqttPacketReader);
  89. }
  90. case MqttControlPacketType.ConnAck:
  91. {
  92. return await DeserializeConnAck(mqttPacketReader);
  93. }
  94. case MqttControlPacketType.Disconnect:
  95. {
  96. return new MqttDisconnectPacket();
  97. }
  98. case MqttControlPacketType.Publish:
  99. {
  100. return await DeserializePublishAsync(mqttPacketReader);
  101. }
  102. case MqttControlPacketType.PubAck:
  103. {
  104. return new MqttPubAckPacket
  105. {
  106. PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
  107. };
  108. }
  109. case MqttControlPacketType.PubRec:
  110. {
  111. return new MqttPubRecPacket
  112. {
  113. PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
  114. };
  115. }
  116. case MqttControlPacketType.PubRel:
  117. {
  118. return new MqttPubRelPacket
  119. {
  120. PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
  121. };
  122. }
  123. case MqttControlPacketType.PubComp:
  124. {
  125. return new MqttPubCompPacket
  126. {
  127. PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
  128. };
  129. }
  130. case MqttControlPacketType.PingReq:
  131. {
  132. return new MqttPingReqPacket();
  133. }
  134. case MqttControlPacketType.PingResp:
  135. {
  136. return new MqttPingRespPacket();
  137. }
  138. case MqttControlPacketType.Subscribe:
  139. {
  140. return await DeserializeSubscribeAsync(mqttPacketReader);
  141. }
  142. case MqttControlPacketType.SubAck:
  143. {
  144. return await DeserializeSubAck(mqttPacketReader);
  145. }
  146. case MqttControlPacketType.Unsubscibe:
  147. {
  148. return await DeserializeUnsubscribeAsync(mqttPacketReader);
  149. }
  150. case MqttControlPacketType.UnsubAck:
  151. {
  152. return new MqttUnsubAckPacket
  153. {
  154. PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
  155. };
  156. }
  157. default:
  158. {
  159. throw new MqttProtocolViolationException($"Packet type ({(int)mqttPacketReader.ControlPacketType}) not supported.");
  160. }
  161. }
  162. }
  163. }
  164. private static async Task<MqttBasePacket> DeserializeUnsubscribeAsync(MqttPacketReader reader)
  165. {
  166. var packet = new MqttUnsubscribePacket
  167. {
  168. PacketIdentifier = await reader.ReadRemainingDataUShortAsync(),
  169. };
  170. while (!reader.EndOfRemainingData)
  171. {
  172. packet.TopicFilters.Add(await reader.ReadRemainingDataStringWithLengthPrefixAsync());
  173. }
  174. return packet;
  175. }
  176. private static async Task<MqttBasePacket> DeserializeSubscribeAsync(MqttPacketReader reader)
  177. {
  178. var packet = new MqttSubscribePacket
  179. {
  180. PacketIdentifier = await reader.ReadRemainingDataUShortAsync(),
  181. };
  182. while (!reader.EndOfRemainingData)
  183. {
  184. packet.TopicFilters.Add(new TopicFilter(
  185. await reader.ReadRemainingDataStringWithLengthPrefixAsync(),
  186. (MqttQualityOfServiceLevel)await reader.ReadRemainingDataByteAsync()));
  187. }
  188. return packet;
  189. }
  190. private static async Task<MqttBasePacket> DeserializePublishAsync(MqttPacketReader reader)
  191. {
  192. var fixedHeader = new ByteReader(reader.FixedHeader);
  193. var retain = fixedHeader.Read();
  194. var qualityOfServiceLevel = (MqttQualityOfServiceLevel)fixedHeader.Read(2);
  195. var dup = fixedHeader.Read();
  196. var topic = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
  197. ushort packetIdentifier = 0;
  198. if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  199. {
  200. packetIdentifier = await reader.ReadRemainingDataUShortAsync();
  201. }
  202. var packet = new MqttPublishPacket
  203. {
  204. Retain = retain,
  205. QualityOfServiceLevel = qualityOfServiceLevel,
  206. Dup = dup,
  207. Topic = topic,
  208. Payload = await reader.ReadRemainingDataAsync(),
  209. PacketIdentifier = packetIdentifier
  210. };
  211. return packet;
  212. }
  213. private static async Task<MqttBasePacket> DeserializeConnectAsync(MqttPacketReader reader)
  214. {
  215. await reader.ReadRemainingDataAsync(2); // Skip 2 bytes
  216. MqttProtocolVersion protocolVersion;
  217. var protocolName = await reader.ReadRemainingDataAsync(4);
  218. if (protocolName.SequenceEqual(ProtocolVersionV310Name))
  219. {
  220. await reader.ReadRemainingDataAsync(2);
  221. protocolVersion = MqttProtocolVersion.V310;
  222. }
  223. else if (protocolName.SequenceEqual(ProtocolVersionV311Name))
  224. {
  225. protocolVersion = MqttProtocolVersion.V311;
  226. }
  227. else
  228. {
  229. throw new MqttProtocolViolationException("Protocol name is not supported.");
  230. }
  231. var protocolLevel = await reader.ReadRemainingDataByteAsync();
  232. var connectFlags = await reader.ReadRemainingDataByteAsync();
  233. var connectFlagsReader = new ByteReader(connectFlags);
  234. connectFlagsReader.Read(); // Reserved.
  235. var packet = new MqttConnectPacket
  236. {
  237. ProtocolVersion = protocolVersion,
  238. CleanSession = connectFlagsReader.Read()
  239. };
  240. var willFlag = connectFlagsReader.Read();
  241. var willQoS = connectFlagsReader.Read(2);
  242. var willRetain = connectFlagsReader.Read();
  243. var passwordFlag = connectFlagsReader.Read();
  244. var usernameFlag = connectFlagsReader.Read();
  245. packet.KeepAlivePeriod = await reader.ReadRemainingDataUShortAsync();
  246. packet.ClientId = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
  247. if (willFlag)
  248. {
  249. packet.WillMessage = new MqttApplicationMessage(
  250. await reader.ReadRemainingDataStringWithLengthPrefixAsync(),
  251. await reader.ReadRemainingDataWithLengthPrefixAsync(),
  252. (MqttQualityOfServiceLevel)willQoS,
  253. willRetain);
  254. }
  255. if (usernameFlag)
  256. {
  257. packet.Username = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
  258. }
  259. if (passwordFlag)
  260. {
  261. packet.Password = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
  262. }
  263. ValidateConnectPacket(packet);
  264. return packet;
  265. }
  266. private static async Task<MqttBasePacket> DeserializeSubAck(MqttPacketReader reader)
  267. {
  268. var packet = new MqttSubAckPacket
  269. {
  270. PacketIdentifier = await reader.ReadRemainingDataUShortAsync()
  271. };
  272. while (!reader.EndOfRemainingData)
  273. {
  274. packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)await reader.ReadRemainingDataByteAsync());
  275. }
  276. return packet;
  277. }
  278. private static async Task<MqttBasePacket> DeserializeConnAck(MqttPacketReader reader)
  279. {
  280. var variableHeader1 = await reader.ReadRemainingDataByteAsync();
  281. var variableHeader2 = await reader.ReadRemainingDataByteAsync();
  282. var packet = new MqttConnAckPacket
  283. {
  284. IsSessionPresent = new ByteReader(variableHeader1).Read(),
  285. ConnectReturnCode = (MqttConnectReturnCode)variableHeader2
  286. };
  287. return packet;
  288. }
  289. private static void ValidateConnectPacket(MqttConnectPacket packet)
  290. {
  291. if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
  292. {
  293. throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
  294. }
  295. }
  296. private static void ValidatePublishPacket(MqttPublishPacket packet)
  297. {
  298. if (packet.QualityOfServiceLevel == 0 && packet.Dup)
  299. {
  300. throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
  301. }
  302. }
  303. private Task SerializeAsync(MqttConnectPacket packet, IMqttCommunicationChannel destination)
  304. {
  305. ValidateConnectPacket(packet);
  306. using (var output = new MqttPacketWriter())
  307. {
  308. // Write variable header
  309. output.Write(0x00, 0x04); // 3.1.2.1 Protocol Name
  310. if (ProtocolVersion == MqttProtocolVersion.V311)
  311. {
  312. output.Write(ProtocolVersionV311Name);
  313. output.Write(0x04); // 3.1.2.2 Protocol Level (4)
  314. }
  315. else
  316. {
  317. output.Write(ProtocolVersionV310Name);
  318. output.Write(0x64);
  319. output.Write(0x70);
  320. output.Write(0x03); // Protocol Level (3)
  321. }
  322. var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags
  323. connectFlags.Write(false); // Reserved
  324. connectFlags.Write(packet.CleanSession);
  325. connectFlags.Write(packet.WillMessage != null);
  326. if (packet.WillMessage != null)
  327. {
  328. connectFlags.Write((int)packet.WillMessage.QualityOfServiceLevel, 2);
  329. connectFlags.Write(packet.WillMessage.Retain);
  330. }
  331. else
  332. {
  333. connectFlags.Write(0, 2);
  334. connectFlags.Write(false);
  335. }
  336. connectFlags.Write(packet.Password != null);
  337. connectFlags.Write(packet.Username != null);
  338. output.Write(connectFlags);
  339. output.Write(packet.KeepAlivePeriod);
  340. output.WriteWithLengthPrefix(packet.ClientId);
  341. if (packet.WillMessage != null)
  342. {
  343. output.WriteWithLengthPrefix(packet.WillMessage.Topic);
  344. output.WriteWithLengthPrefix(packet.WillMessage.Payload);
  345. }
  346. if (packet.Username != null)
  347. {
  348. output.WriteWithLengthPrefix(packet.Username);
  349. }
  350. if (packet.Password != null)
  351. {
  352. output.WriteWithLengthPrefix(packet.Password);
  353. }
  354. output.InjectFixedHeader(MqttControlPacketType.Connect);
  355. return output.WriteToAsync(destination);
  356. }
  357. }
  358. private Task SerializeAsync(MqttConnAckPacket packet, IMqttCommunicationChannel destination)
  359. {
  360. using (var output = new MqttPacketWriter())
  361. {
  362. var connectAcknowledgeFlags = new ByteWriter();
  363. if (ProtocolVersion == MqttProtocolVersion.V311)
  364. {
  365. connectAcknowledgeFlags.Write(packet.IsSessionPresent);
  366. }
  367. output.Write(connectAcknowledgeFlags);
  368. output.Write((byte)packet.ConnectReturnCode);
  369. output.InjectFixedHeader(MqttControlPacketType.ConnAck);
  370. return output.WriteToAsync(destination);
  371. }
  372. }
  373. private static async Task SerializeAsync(MqttPubRelPacket packet, IMqttCommunicationChannel destination)
  374. {
  375. using (var output = new MqttPacketWriter())
  376. {
  377. output.Write(packet.PacketIdentifier);
  378. output.InjectFixedHeader(MqttControlPacketType.PubRel, 0x02);
  379. await output.WriteToAsync(destination);
  380. }
  381. }
  382. private static Task SerializeAsync(MqttDisconnectPacket packet, IMqttCommunicationChannel destination)
  383. {
  384. return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, destination);
  385. }
  386. private static Task SerializeAsync(MqttPingReqPacket packet, IMqttCommunicationChannel destination)
  387. {
  388. return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, destination);
  389. }
  390. private static Task SerializeAsync(MqttPingRespPacket packet, IMqttCommunicationChannel destination)
  391. {
  392. return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, destination);
  393. }
  394. private static Task SerializeAsync(MqttPublishPacket packet, IMqttCommunicationChannel destination)
  395. {
  396. ValidatePublishPacket(packet);
  397. using (var output = new MqttPacketWriter())
  398. {
  399. output.WriteWithLengthPrefix(packet.Topic);
  400. if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  401. {
  402. output.Write(packet.PacketIdentifier);
  403. }
  404. else
  405. {
  406. if (packet.PacketIdentifier > 0)
  407. {
  408. throw new MqttProtocolViolationException("Packet identifier must be empty if QoS == 0 [MQTT-2.3.1-5].");
  409. }
  410. }
  411. if (packet.Payload?.Length > 0)
  412. {
  413. output.Write(packet.Payload);
  414. }
  415. var fixedHeader = new ByteWriter();
  416. fixedHeader.Write(packet.Retain);
  417. fixedHeader.Write((byte)packet.QualityOfServiceLevel, 2);
  418. fixedHeader.Write(packet.Dup);
  419. output.InjectFixedHeader(MqttControlPacketType.Publish, fixedHeader.Value);
  420. return output.WriteToAsync(destination);
  421. }
  422. }
  423. private static Task SerializeAsync(MqttPubAckPacket packet, IMqttCommunicationChannel destination)
  424. {
  425. using (var output = new MqttPacketWriter())
  426. {
  427. output.Write(packet.PacketIdentifier);
  428. output.InjectFixedHeader(MqttControlPacketType.PubAck);
  429. return output.WriteToAsync(destination);
  430. }
  431. }
  432. private static Task SerializeAsync(MqttPubRecPacket packet, IMqttCommunicationChannel destination)
  433. {
  434. using (var output = new MqttPacketWriter())
  435. {
  436. output.Write(packet.PacketIdentifier);
  437. output.InjectFixedHeader(MqttControlPacketType.PubRec);
  438. return output.WriteToAsync(destination);
  439. }
  440. }
  441. private static Task SerializeAsync(MqttPubCompPacket packet, IMqttCommunicationChannel destination)
  442. {
  443. using (var output = new MqttPacketWriter())
  444. {
  445. output.Write(packet.PacketIdentifier);
  446. output.InjectFixedHeader(MqttControlPacketType.PubComp);
  447. return output.WriteToAsync(destination);
  448. }
  449. }
  450. private static Task SerializeAsync(MqttSubscribePacket packet, IMqttCommunicationChannel destination)
  451. {
  452. using (var output = new MqttPacketWriter())
  453. {
  454. output.Write(packet.PacketIdentifier);
  455. if (packet.TopicFilters?.Count > 0)
  456. {
  457. foreach (var topicFilter in packet.TopicFilters)
  458. {
  459. output.WriteWithLengthPrefix(topicFilter.Topic);
  460. output.Write((byte)topicFilter.QualityOfServiceLevel);
  461. }
  462. }
  463. output.InjectFixedHeader(MqttControlPacketType.Subscribe, 0x02);
  464. return output.WriteToAsync(destination);
  465. }
  466. }
  467. private static Task SerializeAsync(MqttSubAckPacket packet, IMqttCommunicationChannel destination)
  468. {
  469. using (var output = new MqttPacketWriter())
  470. {
  471. output.Write(packet.PacketIdentifier);
  472. if (packet.SubscribeReturnCodes?.Any() == true)
  473. {
  474. foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
  475. {
  476. output.Write((byte)packetSubscribeReturnCode);
  477. }
  478. }
  479. output.InjectFixedHeader(MqttControlPacketType.SubAck);
  480. return output.WriteToAsync(destination);
  481. }
  482. }
  483. private static Task SerializeAsync(MqttUnsubscribePacket packet, IMqttCommunicationChannel destination)
  484. {
  485. using (var output = new MqttPacketWriter())
  486. {
  487. output.Write(packet.PacketIdentifier);
  488. if (packet.TopicFilters?.Any() == true)
  489. {
  490. foreach (var topicFilter in packet.TopicFilters)
  491. {
  492. output.WriteWithLengthPrefix(topicFilter);
  493. }
  494. }
  495. output.InjectFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
  496. return output.WriteToAsync(destination);
  497. }
  498. }
  499. private static Task SerializeAsync(MqttUnsubAckPacket packet, IMqttCommunicationChannel destination)
  500. {
  501. using (var output = new MqttPacketWriter())
  502. {
  503. output.Write(packet.PacketIdentifier);
  504. output.InjectFixedHeader(MqttControlPacketType.UnsubAck);
  505. return output.WriteToAsync(destination);
  506. }
  507. }
  508. private static Task SerializeEmptyPacketAsync(MqttControlPacketType type, IMqttCommunicationChannel destination)
  509. {
  510. using (var output = new MqttPacketWriter())
  511. {
  512. output.InjectFixedHeader(type);
  513. return output.WriteToAsync(destination);
  514. }
  515. }
  516. }
  517. }