MqttClient.cs 120 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630
  1. /*
  2. Copyright (c) 2013, 2014 Paolo Patierno
  3. All rights reserved. This program and the accompanying materials
  4. are made available under the terms of the Eclipse Public License v1.0
  5. and Eclipse Distribution License v1.0 which accompany this distribution.
  6. The Eclipse Public License is available at
  7. http://www.eclipse.org/legal/epl-v10.html
  8. and the Eclipse Distribution License is available at
  9. http://www.eclipse.org/org/documents/edl-v10.php.
  10. Contributors:
  11. Paolo Patierno - initial API and implementation and/or initial documentation
  12. */
  13. using System;
  14. using System.Net;
  15. #if !(WINDOWS_APP || WINDOWS_PHONE_APP)
  16. using System.Net.Sockets;
  17. using System.Security.Cryptography.X509Certificates;
  18. #endif
  19. using System.Threading;
  20. using uPLibrary.Networking.M2Mqtt.Exceptions;
  21. using uPLibrary.Networking.M2Mqtt.Messages;
  22. using uPLibrary.Networking.M2Mqtt.Session;
  23. using uPLibrary.Networking.M2Mqtt.Utility;
  24. using uPLibrary.Networking.M2Mqtt.Internal;
  25. // if .Net Micro Framework
  26. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  27. using Microsoft.SPOT;
  28. #if SSL
  29. using Microsoft.SPOT.Net.Security;
  30. #endif
  31. // else other frameworks (.Net, .Net Compact, Mono, Windows Phone)
  32. #else
  33. using System.Collections.Generic;
  34. #if (SSL && !(WINDOWS_APP || WINDOWS_PHONE_APP))
  35. using System.Security.Authentication;
  36. using System.Net.Security;
  37. #endif
  38. #endif
  39. #if (WINDOWS_APP || WINDOWS_PHONE_APP)
  40. using Windows.Networking.Sockets;
  41. #endif
  42. using System.Collections;
  43. // alias needed due to Microsoft.SPOT.Trace in .Net Micro Framework
  44. // (it's ambiguos with uPLibrary.Networking.M2Mqtt.Utility.Trace)
  45. using MqttUtility = uPLibrary.Networking.M2Mqtt.Utility;
  46. namespace uPLibrary.Networking.M2Mqtt
  47. {
  48. /// <summary>
  49. /// MQTT Client
  50. /// </summary>
  51. public class MqttClient
  52. {
  53. #if BROKER
  54. #region Constants ...
  55. // thread names
  56. private const string RECEIVE_THREAD_NAME = "ReceiveThread";
  57. private const string RECEIVE_EVENT_THREAD_NAME = "DispatchEventThread";
  58. private const string PROCESS_INFLIGHT_THREAD_NAME = "ProcessInflightThread";
  59. private const string KEEP_ALIVE_THREAD = "KeepAliveThread";
  60. #endregion
  61. #endif
  62. /// <summary>
  63. /// Delagate that defines event handler for PUBLISH message received
  64. /// </summary>
  65. public delegate void MqttMsgPublishEventHandler(object sender, MqttMsgPublishEventArgs e);
  66. /// <summary>
  67. /// Delegate that defines event handler for published message
  68. /// </summary>
  69. public delegate void MqttMsgPublishedEventHandler(object sender, MqttMsgPublishedEventArgs e);
  70. /// <summary>
  71. /// Delagate that defines event handler for subscribed topic
  72. /// </summary>
  73. public delegate void MqttMsgSubscribedEventHandler(object sender, MqttMsgSubscribedEventArgs e);
  74. /// <summary>
  75. /// Delagate that defines event handler for unsubscribed topic
  76. /// </summary>
  77. public delegate void MqttMsgUnsubscribedEventHandler(object sender, MqttMsgUnsubscribedEventArgs e);
  78. #if BROKER
  79. /// <summary>
  80. /// Delagate that defines event handler for SUBSCRIBE message received
  81. /// </summary>
  82. public delegate void MqttMsgSubscribeEventHandler(object sender, MqttMsgSubscribeEventArgs e);
  83. /// <summary>
  84. /// Delagate that defines event handler for UNSUBSCRIBE message received
  85. /// </summary>
  86. public delegate void MqttMsgUnsubscribeEventHandler(object sender, MqttMsgUnsubscribeEventArgs e);
  87. /// <summary>
  88. /// Delagate that defines event handler for CONNECT message received
  89. /// </summary>
  90. public delegate void MqttMsgConnectEventHandler(object sender, MqttMsgConnectEventArgs e);
  91. /// <summary>
  92. /// Delegate that defines event handler for client disconnection (DISCONNECT message or not)
  93. /// </summary>
  94. public delegate void MqttMsgDisconnectEventHandler(object sender, EventArgs e);
  95. #endif
  96. /// <summary>
  97. /// Delegate that defines event handler for cliet/peer disconnection
  98. /// </summary>
  99. public delegate void ConnectionClosedEventHandler(object sender, EventArgs e);
  100. // broker hostname (or ip address) and port
  101. private string brokerHostName;
  102. private int brokerPort;
  103. // running status of threads
  104. private bool isRunning;
  105. // event for raising received message event
  106. private AutoResetEvent receiveEventWaitHandle;
  107. // event for starting process inflight queue asynchronously
  108. private AutoResetEvent inflightWaitHandle;
  109. // event for signaling synchronous receive
  110. AutoResetEvent syncEndReceiving;
  111. // message received
  112. MqttMsgBase msgReceived;
  113. // exeption thrown during receiving
  114. Exception exReceiving;
  115. // keep alive period (in ms)
  116. private int keepAlivePeriod;
  117. // events for signaling on keep alive thread
  118. private AutoResetEvent keepAliveEvent;
  119. private AutoResetEvent keepAliveEventEnd;
  120. // last communication time in ticks
  121. private int lastCommTime;
  122. // event for PUBLISH message received
  123. public event MqttMsgPublishEventHandler MqttMsgPublishReceived;
  124. // event for published message
  125. public event MqttMsgPublishedEventHandler MqttMsgPublished;
  126. // event for subscribed topic
  127. public event MqttMsgSubscribedEventHandler MqttMsgSubscribed;
  128. // event for unsubscribed topic
  129. public event MqttMsgUnsubscribedEventHandler MqttMsgUnsubscribed;
  130. #if BROKER
  131. // event for SUBSCRIBE message received
  132. public event MqttMsgSubscribeEventHandler MqttMsgSubscribeReceived;
  133. // event for USUBSCRIBE message received
  134. public event MqttMsgUnsubscribeEventHandler MqttMsgUnsubscribeReceived;
  135. // event for CONNECT message received
  136. public event MqttMsgConnectEventHandler MqttMsgConnected;
  137. // event for DISCONNECT message received
  138. public event MqttMsgDisconnectEventHandler MqttMsgDisconnected;
  139. #endif
  140. // event for peer/client disconnection
  141. public event ConnectionClosedEventHandler ConnectionClosed;
  142. // channel to communicate over the network
  143. private IMqttNetworkChannel channel;
  144. // inflight messages queue
  145. private Queue inflightQueue;
  146. // internal queue for received messages about inflight messages
  147. private Queue internalQueue;
  148. // internal queue for dispatching events
  149. private Queue eventQueue;
  150. // session
  151. private MqttClientSession session;
  152. // reference to avoid access to singleton via property
  153. private MqttSettings settings;
  154. // current message identifier generated
  155. private ushort messageIdCounter = 0;
  156. // connection is closing due to peer
  157. private bool isConnectionClosing;
  158. /// <summary>
  159. /// Connection status between client and broker
  160. /// </summary>
  161. public bool IsConnected { get; private set; }
  162. /// <summary>
  163. /// Client identifier
  164. /// </summary>
  165. public string ClientId { get; private set; }
  166. /// <summary>
  167. /// Clean session flag
  168. /// </summary>
  169. public bool CleanSession { get; private set; }
  170. /// <summary>
  171. /// Will flag
  172. /// </summary>
  173. public bool WillFlag { get; private set; }
  174. /// <summary>
  175. /// Will QOS level
  176. /// </summary>
  177. public byte WillQosLevel { get; private set; }
  178. /// <summary>
  179. /// Will topic
  180. /// </summary>
  181. public string WillTopic { get; private set; }
  182. /// <summary>
  183. /// Will message
  184. /// </summary>
  185. public string WillMessage { get; private set; }
  186. /// <summary>
  187. /// MQTT protocol version
  188. /// </summary>
  189. public MqttProtocolVersion ProtocolVersion { get; set; }
  190. #if BROKER
  191. /// <summary>
  192. /// MQTT Client Session
  193. /// </summary>
  194. public MqttClientSession Session
  195. {
  196. get { return this.session; }
  197. set { this.session = value; }
  198. }
  199. #endif
  200. /// <summary>
  201. /// MQTT client settings
  202. /// </summary>
  203. public MqttSettings Settings
  204. {
  205. get { return this.settings; }
  206. }
  207. #if !(WINDOWS_APP || WINDOWS_PHONE_APP)
  208. /// <summary>
  209. /// Constructor
  210. /// </summary>
  211. /// <param name="brokerIpAddress">Broker IP address</param>
  212. [Obsolete("Use this ctor MqttClient(string brokerHostName) insted")]
  213. public MqttClient(IPAddress brokerIpAddress) :
  214. this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None)
  215. {
  216. }
  217. /// <summary>
  218. /// Constructor
  219. /// </summary>
  220. /// <param name="brokerIpAddress">Broker IP address</param>
  221. /// <param name="brokerPort">Broker port</param>
  222. /// <param name="secure">Using secure connection</param>
  223. /// <param name="caCert">CA certificate for secure connection</param>
  224. /// <param name="clientCert">Client certificate</param>
  225. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  226. [Obsolete("Use this ctor MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert) insted")]
  227. public MqttClient(IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
  228. {
  229. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  230. this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol, null, null);
  231. #else
  232. this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol);
  233. #endif
  234. }
  235. #endif
  236. /// <summary>
  237. /// Constructor
  238. /// </summary>
  239. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  240. public MqttClient(string brokerHostName) :
  241. #if !(WINDOWS_APP || WINDOWS_PHONE_APP)
  242. this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None)
  243. #else
  244. this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, MqttSslProtocols.None)
  245. #endif
  246. {
  247. }
  248. /// <summary>
  249. /// Constructor
  250. /// </summary>
  251. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  252. /// <param name="brokerPort">Broker port</param>
  253. /// <param name="secure">Using secure connection</param>
  254. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  255. #if !(WINDOWS_APP || WINDOWS_PHONE_APP)
  256. /// <param name="caCert">CA certificate for secure connection</param>
  257. /// <param name="clientCert">Client certificate</param>
  258. public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
  259. #else
  260. public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
  261. #endif
  262. {
  263. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
  264. this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, null, null);
  265. #elif (WINDOWS_APP || WINDOWS_PHONE_APP)
  266. this.Init(brokerHostName, brokerPort, secure, sslProtocol);
  267. #else
  268. this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol);
  269. #endif
  270. }
  271. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
  272. /// <summary>
  273. /// Constructor
  274. /// </summary>
  275. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  276. /// <param name="brokerPort">Broker port</param>
  277. /// <param name="secure">Using secure connection</param>
  278. /// <param name="caCert">CA certificate for secure connection</param>
  279. /// <param name="clientCert">Client certificate</param>
  280. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  281. /// <param name="userCertificateValidationCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  282. public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
  283. RemoteCertificateValidationCallback userCertificateValidationCallback)
  284. : this(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, null)
  285. {
  286. }
  287. /// <summary>
  288. /// Constructor
  289. /// </summary>
  290. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  291. /// <param name="brokerPort">Broker port</param>
  292. /// <param name="secure">Using secure connection</param>
  293. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  294. /// <param name="userCertificateValidationCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  295. /// <param name="userCertificateSelectionCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  296. public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol,
  297. RemoteCertificateValidationCallback userCertificateValidationCallback,
  298. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  299. : this(brokerHostName, brokerPort, secure, null, null, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback)
  300. {
  301. }
  302. /// <summary>
  303. /// Constructor
  304. /// </summary>
  305. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  306. /// <param name="brokerPort">Broker port</param>
  307. /// <param name="secure">Using secure connection</param>
  308. /// <param name="caCert">CA certificate for secure connection</param>
  309. /// <param name="clientCert">Client certificate</param>
  310. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  311. /// <param name="userCertificateValidationCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  312. /// <param name="userCertificateSelectionCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  313. public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
  314. RemoteCertificateValidationCallback userCertificateValidationCallback,
  315. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  316. {
  317. this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback);
  318. }
  319. #endif
  320. #if BROKER
  321. /// <summary>
  322. /// Constructor
  323. /// </summary>
  324. /// <param name="channel">Network channel for communication</param>
  325. public MqttClient(IMqttNetworkChannel channel)
  326. {
  327. // set default MQTT protocol version (default is 3.1.1)
  328. this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
  329. this.channel = channel;
  330. // reference to MQTT settings
  331. this.settings = MqttSettings.Instance;
  332. // client not connected yet (CONNACK not send from client), some default values
  333. this.IsConnected = false;
  334. this.ClientId = null;
  335. this.CleanSession = true;
  336. this.keepAliveEvent = new AutoResetEvent(false);
  337. // queue for handling inflight messages (publishing and acknowledge)
  338. this.inflightWaitHandle = new AutoResetEvent(false);
  339. this.inflightQueue = new Queue();
  340. // queue for received message
  341. this.receiveEventWaitHandle = new AutoResetEvent(false);
  342. this.eventQueue = new Queue();
  343. this.internalQueue = new Queue();
  344. // session
  345. this.session = null;
  346. }
  347. #endif
  348. /// <summary>
  349. /// MqttClient initialization
  350. /// </summary>
  351. /// <param name="brokerHostName">Broker Host Name or IP Address</param>
  352. /// <param name="brokerPort">Broker port</param>
  353. /// <param name="secure">>Using secure connection</param>
  354. /// <param name="caCert">CA certificate for secure connection</param>
  355. /// <param name="clientCert">Client certificate</param>
  356. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  357. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
  358. /// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  359. /// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  360. private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
  361. RemoteCertificateValidationCallback userCertificateValidationCallback,
  362. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  363. #elif (WINDOWS_APP || WINDOWS_PHONE_APP)
  364. private void Init(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
  365. #else
  366. private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
  367. #endif
  368. {
  369. // set default MQTT protocol version (default is 3.1.1)
  370. this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
  371. #if !SSL
  372. // check security parameters
  373. if (secure)
  374. throw new ArgumentException("Library compiled without SSL support");
  375. #endif
  376. this.brokerHostName = brokerHostName;
  377. this.brokerPort = brokerPort;
  378. // reference to MQTT settings
  379. this.settings = MqttSettings.Instance;
  380. // set settings port based on secure connection or not
  381. if (!secure)
  382. this.settings.Port = this.brokerPort;
  383. else
  384. this.settings.SslPort = this.brokerPort;
  385. this.syncEndReceiving = new AutoResetEvent(false);
  386. this.keepAliveEvent = new AutoResetEvent(false);
  387. // queue for handling inflight messages (publishing and acknowledge)
  388. this.inflightWaitHandle = new AutoResetEvent(false);
  389. this.inflightQueue = new Queue();
  390. // queue for received message
  391. this.receiveEventWaitHandle = new AutoResetEvent(false);
  392. this.eventQueue = new Queue();
  393. this.internalQueue = new Queue();
  394. // session
  395. this.session = null;
  396. // create network channel
  397. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
  398. this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback);
  399. #elif (WINDOWS_APP || WINDOWS_PHONE_APP)
  400. this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, sslProtocol);
  401. #else
  402. this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol);
  403. #endif
  404. }
  405. /// <summary>
  406. /// Connect to broker
  407. /// </summary>
  408. /// <param name="clientId">Client identifier</param>
  409. /// <returns>Return code of CONNACK message from broker</returns>
  410. public byte Connect(string clientId)
  411. {
  412. return this.Connect(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
  413. }
  414. /// <summary>
  415. /// Connect to broker
  416. /// </summary>
  417. /// <param name="clientId">Client identifier</param>
  418. /// <param name="username">Username</param>
  419. /// <param name="password">Password</param>
  420. /// <returns>Return code of CONNACK message from broker</returns>
  421. public byte Connect(string clientId,
  422. string username,
  423. string password)
  424. {
  425. return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
  426. }
  427. /// <summary>
  428. /// Connect to broker
  429. /// </summary>
  430. /// <param name="clientId">Client identifier</param>
  431. /// <param name="username">Username</param>
  432. /// <param name="password">Password</param>
  433. /// <param name="cleanSession">Clean sessione flag</param>
  434. /// <param name="keepAlivePeriod">Keep alive period</param>
  435. /// <returns>Return code of CONNACK message from broker</returns>
  436. public byte Connect(string clientId,
  437. string username,
  438. string password,
  439. bool cleanSession,
  440. ushort keepAlivePeriod)
  441. {
  442. return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod);
  443. }
  444. /// <summary>
  445. /// Connect to broker
  446. /// </summary>
  447. /// <param name="clientId">Client identifier</param>
  448. /// <param name="username">Username</param>
  449. /// <param name="password">Password</param>
  450. /// <param name="willRetain">Will retain flag</param>
  451. /// <param name="willQosLevel">Will QOS level</param>
  452. /// <param name="willFlag">Will flag</param>
  453. /// <param name="willTopic">Will topic</param>
  454. /// <param name="willMessage">Will message</param>
  455. /// <param name="cleanSession">Clean sessione flag</param>
  456. /// <param name="keepAlivePeriod">Keep alive period</param>
  457. /// <returns>Return code of CONNACK message from broker</returns>
  458. public byte Connect(string clientId,
  459. string username,
  460. string password,
  461. bool willRetain,
  462. byte willQosLevel,
  463. bool willFlag,
  464. string willTopic,
  465. string willMessage,
  466. bool cleanSession,
  467. ushort keepAlivePeriod)
  468. {
  469. // create CONNECT message
  470. MqttMsgConnect connect = new MqttMsgConnect(clientId,
  471. username,
  472. password,
  473. willRetain,
  474. willQosLevel,
  475. willFlag,
  476. willTopic,
  477. willMessage,
  478. cleanSession,
  479. keepAlivePeriod,
  480. (byte)this.ProtocolVersion);
  481. try
  482. {
  483. // connect to the broker
  484. this.channel.Connect();
  485. }
  486. catch (Exception ex)
  487. {
  488. throw new MqttConnectionException("Exception connecting to the broker", ex);
  489. }
  490. this.lastCommTime = 0;
  491. this.isRunning = true;
  492. this.isConnectionClosing = false;
  493. // start thread for receiving messages from broker
  494. Fx.StartThread(this.ReceiveThread);
  495. MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect);
  496. // if connection accepted, start keep alive timer and
  497. if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED)
  498. {
  499. // set all client properties
  500. this.ClientId = clientId;
  501. this.CleanSession = cleanSession;
  502. this.WillFlag = willFlag;
  503. this.WillTopic = willTopic;
  504. this.WillMessage = willMessage;
  505. this.WillQosLevel = willQosLevel;
  506. this.keepAlivePeriod = keepAlivePeriod * 1000; // convert in ms
  507. // restore previous session
  508. this.RestoreSession();
  509. // keep alive period equals zero means turning off keep alive mechanism
  510. if (this.keepAlivePeriod != 0)
  511. {
  512. // start thread for sending keep alive message to the broker
  513. Fx.StartThread(this.KeepAliveThread);
  514. }
  515. // start thread for raising received message event from broker
  516. Fx.StartThread(this.DispatchEventThread);
  517. // start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge)
  518. Fx.StartThread(this.ProcessInflightThread);
  519. this.IsConnected = true;
  520. }
  521. return connack.ReturnCode;
  522. }
  523. /// <summary>
  524. /// Disconnect from broker
  525. /// </summary>
  526. public void Disconnect()
  527. {
  528. MqttMsgDisconnect disconnect = new MqttMsgDisconnect();
  529. this.Send(disconnect);
  530. // close client
  531. this.OnConnectionClosing();
  532. }
  533. #if BROKER
  534. /// <summary>
  535. /// Open client communication
  536. /// </summary>
  537. public void Open()
  538. {
  539. this.isRunning = true;
  540. // start thread for receiving messages from client
  541. Fx.StartThread(this.ReceiveThread);
  542. // start thread for raising received message event from client
  543. Fx.StartThread(this.DispatchEventThread);
  544. // start thread for handling inflight messages queue to client asynchronously (publish and acknowledge)
  545. Fx.StartThread(this.ProcessInflightThread);
  546. }
  547. #endif
  548. /// <summary>
  549. /// Close client
  550. /// </summary>
  551. #if BROKER
  552. public void Close()
  553. #else
  554. private void Close()
  555. #endif
  556. {
  557. // stop receiving thread
  558. this.isRunning = false;
  559. // wait end receive event thread
  560. if (this.receiveEventWaitHandle != null)
  561. this.receiveEventWaitHandle.Set();
  562. // wait end process inflight thread
  563. if (this.inflightWaitHandle != null)
  564. this.inflightWaitHandle.Set();
  565. #if BROKER
  566. // unlock keep alive thread
  567. this.keepAliveEvent.Set();
  568. #else
  569. // unlock keep alive thread and wait
  570. this.keepAliveEvent.Set();
  571. if (this.keepAliveEventEnd != null)
  572. this.keepAliveEventEnd.WaitOne();
  573. #endif
  574. // clear all queues
  575. this.inflightQueue.Clear();
  576. this.internalQueue.Clear();
  577. this.eventQueue.Clear();
  578. // close network channel
  579. this.channel.Close();
  580. this.IsConnected = false;
  581. }
  582. /// <summary>
  583. /// Execute ping to broker for keep alive
  584. /// </summary>
  585. /// <returns>PINGRESP message from broker</returns>
  586. private MqttMsgPingResp Ping()
  587. {
  588. MqttMsgPingReq pingreq = new MqttMsgPingReq();
  589. try
  590. {
  591. // broker must send PINGRESP within timeout equal to keep alive period
  592. return (MqttMsgPingResp)this.SendReceive(pingreq, this.keepAlivePeriod);
  593. }
  594. catch (Exception e)
  595. {
  596. #if TRACE
  597. MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
  598. #endif
  599. // client must close connection
  600. this.OnConnectionClosing();
  601. return null;
  602. }
  603. }
  604. #if BROKER
  605. /// <summary>
  606. /// Send CONNACK message to the client (connection accepted or not)
  607. /// </summary>
  608. /// <param name="connect">CONNECT message with all client information</param>
  609. /// <param name="returnCode">Return code for CONNACK message</param>
  610. /// <param name="clientId">If not null, client id assigned by broker</param>
  611. /// <param name="sessionPresent">Session present on the broker</param>
  612. public void Connack(MqttMsgConnect connect, byte returnCode, string clientId, bool sessionPresent)
  613. {
  614. this.lastCommTime = 0;
  615. // create CONNACK message and ...
  616. MqttMsgConnack connack = new MqttMsgConnack();
  617. connack.ReturnCode = returnCode;
  618. // [v3.1.1] session present flag
  619. if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1_1)
  620. connack.SessionPresent = sessionPresent;
  621. // ... send it to the client
  622. this.Send(connack);
  623. // connection accepted, start keep alive thread checking
  624. if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED)
  625. {
  626. // [v3.1.1] if client id isn't null, the CONNECT message has a cliend id with zero bytes length
  627. // and broker assigned a unique identifier to the client
  628. this.ClientId = (clientId == null) ? connect.ClientId : clientId;
  629. this.CleanSession = connect.CleanSession;
  630. this.WillFlag = connect.WillFlag;
  631. this.WillTopic = connect.WillTopic;
  632. this.WillMessage = connect.WillMessage;
  633. this.WillQosLevel = connect.WillQosLevel;
  634. this.keepAlivePeriod = connect.KeepAlivePeriod * 1000; // convert in ms
  635. // broker has a tolerance of 1.5 specified keep alive period
  636. this.keepAlivePeriod += (this.keepAlivePeriod / 2);
  637. // start thread for checking keep alive period timeout
  638. Fx.StartThread(this.KeepAliveThread);
  639. this.isConnectionClosing = false;
  640. this.IsConnected = true;
  641. }
  642. // connection refused, close TCP/IP channel
  643. else
  644. {
  645. this.Close();
  646. }
  647. }
  648. /// <summary>
  649. /// Send SUBACK message to the client
  650. /// </summary>
  651. /// <param name="messageId">Message Id for the SUBSCRIBE message that is being acknowledged</param>
  652. /// <param name="grantedQosLevels">Granted QoS Levels</param>
  653. public void Suback(ushort messageId, byte[] grantedQosLevels)
  654. {
  655. MqttMsgSuback suback = new MqttMsgSuback();
  656. suback.MessageId = messageId;
  657. suback.GrantedQoSLevels = grantedQosLevels;
  658. this.Send(suback);
  659. }
  660. /// <summary>
  661. /// Send UNSUBACK message to the client
  662. /// </summary>
  663. /// <param name="messageId">Message Id for the UNSUBSCRIBE message that is being acknowledged</param>
  664. public void Unsuback(ushort messageId)
  665. {
  666. MqttMsgUnsuback unsuback = new MqttMsgUnsuback();
  667. unsuback.MessageId = messageId;
  668. this.Send(unsuback);
  669. }
  670. #endif
  671. /// <summary>
  672. /// Subscribe for message topics
  673. /// </summary>
  674. /// <param name="topics">List of topics to subscribe</param>
  675. /// <param name="qosLevels">QOS levels related to topics</param>
  676. /// <returns>Message Id related to SUBSCRIBE message</returns>
  677. public ushort Subscribe(string[] topics, byte[] qosLevels)
  678. {
  679. MqttMsgSubscribe subscribe =
  680. new MqttMsgSubscribe(topics, qosLevels);
  681. subscribe.MessageId = this.GetMessageId();
  682. // enqueue subscribe request into the inflight queue
  683. this.EnqueueInflight(subscribe, MqttMsgFlow.ToPublish);
  684. return subscribe.MessageId;
  685. }
  686. /// <summary>
  687. /// Unsubscribe for message topics
  688. /// </summary>
  689. /// <param name="topics">List of topics to unsubscribe</param>
  690. /// <returns>Message Id in UNSUBACK message from broker</returns>
  691. public ushort Unsubscribe(string[] topics)
  692. {
  693. MqttMsgUnsubscribe unsubscribe =
  694. new MqttMsgUnsubscribe(topics);
  695. unsubscribe.MessageId = this.GetMessageId();
  696. // enqueue unsubscribe request into the inflight queue
  697. this.EnqueueInflight(unsubscribe, MqttMsgFlow.ToPublish);
  698. return unsubscribe.MessageId;
  699. }
  700. /// <summary>
  701. /// Publish a message asynchronously (QoS Level 0 and not retained)
  702. /// </summary>
  703. /// <param name="topic">Message topic</param>
  704. /// <param name="message">Message data (payload)</param>
  705. /// <returns>Message Id related to PUBLISH message</returns>
  706. public ushort Publish(string topic, byte[] message)
  707. {
  708. return this.Publish(topic, message, MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, false);
  709. }
  710. /// <summary>
  711. /// Publish a message asynchronously
  712. /// </summary>
  713. /// <param name="topic">Message topic</param>
  714. /// <param name="message">Message data (payload)</param>
  715. /// <param name="qosLevel">QoS Level</param>
  716. /// <param name="retain">Retain flag</param>
  717. /// <returns>Message Id related to PUBLISH message</returns>
  718. public ushort Publish(string topic, byte[] message, byte qosLevel, bool retain)
  719. {
  720. MqttMsgPublish publish =
  721. new MqttMsgPublish(topic, message, false, qosLevel, retain);
  722. publish.MessageId = this.GetMessageId();
  723. // enqueue message to publish into the inflight queue
  724. bool enqueue = this.EnqueueInflight(publish, MqttMsgFlow.ToPublish);
  725. // message enqueued
  726. if (enqueue)
  727. return publish.MessageId;
  728. // infligh queue full, message not enqueued
  729. else
  730. throw new MqttClientException(MqttClientErrorCode.InflightQueueFull);
  731. }
  732. /// <summary>
  733. /// Wrapper method for raising events
  734. /// </summary>
  735. /// <param name="internalEvent">Internal event</param>
  736. private void OnInternalEvent(InternalEvent internalEvent)
  737. {
  738. lock (this.eventQueue)
  739. {
  740. this.eventQueue.Enqueue(internalEvent);
  741. }
  742. this.receiveEventWaitHandle.Set();
  743. }
  744. /// <summary>
  745. /// Wrapper method for raising closing connection event
  746. /// </summary>
  747. private void OnConnectionClosing()
  748. {
  749. if (!this.isConnectionClosing)
  750. {
  751. this.isConnectionClosing = true;
  752. this.receiveEventWaitHandle.Set();
  753. }
  754. }
  755. /// <summary>
  756. /// Wrapper method for raising PUBLISH message received event
  757. /// </summary>
  758. /// <param name="publish">PUBLISH message received</param>
  759. private void OnMqttMsgPublishReceived(MqttMsgPublish publish)
  760. {
  761. if (this.MqttMsgPublishReceived != null)
  762. {
  763. this.MqttMsgPublishReceived(this,
  764. new MqttMsgPublishEventArgs(publish.Topic, publish.Message, publish.DupFlag, publish.QosLevel, publish.Retain));
  765. }
  766. }
  767. /// <summary>
  768. /// Wrapper method for raising published message event
  769. /// </summary>
  770. /// <param name="messageId">Message identifier for published message</param>
  771. /// <param name="isPublished">Publish flag</param>
  772. private void OnMqttMsgPublished(ushort messageId, bool isPublished)
  773. {
  774. if (this.MqttMsgPublished != null)
  775. {
  776. this.MqttMsgPublished(this,
  777. new MqttMsgPublishedEventArgs(messageId, isPublished));
  778. }
  779. }
  780. /// <summary>
  781. /// Wrapper method for raising subscribed topic event
  782. /// </summary>
  783. /// <param name="suback">SUBACK message received</param>
  784. private void OnMqttMsgSubscribed(MqttMsgSuback suback)
  785. {
  786. if (this.MqttMsgSubscribed != null)
  787. {
  788. this.MqttMsgSubscribed(this,
  789. new MqttMsgSubscribedEventArgs(suback.MessageId, suback.GrantedQoSLevels));
  790. }
  791. }
  792. /// <summary>
  793. /// Wrapper method for raising unsubscribed topic event
  794. /// </summary>
  795. /// <param name="messageId">Message identifier for unsubscribed topic</param>
  796. private void OnMqttMsgUnsubscribed(ushort messageId)
  797. {
  798. if (this.MqttMsgUnsubscribed != null)
  799. {
  800. this.MqttMsgUnsubscribed(this,
  801. new MqttMsgUnsubscribedEventArgs(messageId));
  802. }
  803. }
  804. #if BROKER
  805. /// <summary>
  806. /// Wrapper method for raising SUBSCRIBE message event
  807. /// </summary>
  808. /// <param name="messageId">Message identifier for subscribe topics request</param>
  809. /// <param name="topics">Topics requested to subscribe</param>
  810. /// <param name="qosLevels">List of QOS Levels requested</param>
  811. private void OnMqttMsgSubscribeReceived(ushort messageId, string[] topics, byte[] qosLevels)
  812. {
  813. if (this.MqttMsgSubscribeReceived != null)
  814. {
  815. this.MqttMsgSubscribeReceived(this,
  816. new MqttMsgSubscribeEventArgs(messageId, topics, qosLevels));
  817. }
  818. }
  819. /// <summary>
  820. /// Wrapper method for raising UNSUBSCRIBE message event
  821. /// </summary>
  822. /// <param name="messageId">Message identifier for unsubscribe topics request</param>
  823. /// <param name="topics">Topics requested to unsubscribe</param>
  824. private void OnMqttMsgUnsubscribeReceived(ushort messageId, string[] topics)
  825. {
  826. if (this.MqttMsgUnsubscribeReceived != null)
  827. {
  828. this.MqttMsgUnsubscribeReceived(this,
  829. new MqttMsgUnsubscribeEventArgs(messageId, topics));
  830. }
  831. }
  832. /// <summary>
  833. /// Wrapper method for raising CONNECT message event
  834. /// </summary>
  835. private void OnMqttMsgConnected(MqttMsgConnect connect)
  836. {
  837. if (this.MqttMsgConnected != null)
  838. {
  839. this.ProtocolVersion = (MqttProtocolVersion)connect.ProtocolVersion;
  840. this.MqttMsgConnected(this, new MqttMsgConnectEventArgs(connect));
  841. }
  842. }
  843. /// <summary>
  844. /// Wrapper method for raising DISCONNECT message event
  845. /// </summary>
  846. private void OnMqttMsgDisconnected()
  847. {
  848. if (this.MqttMsgDisconnected != null)
  849. {
  850. this.MqttMsgDisconnected(this, EventArgs.Empty);
  851. }
  852. }
  853. #endif
  854. /// <summary>
  855. /// Wrapper method for peer/client disconnection
  856. /// </summary>
  857. private void OnConnectionClosed()
  858. {
  859. if (this.ConnectionClosed != null)
  860. {
  861. this.ConnectionClosed(this, EventArgs.Empty);
  862. }
  863. }
  864. /// <summary>
  865. /// Send a message
  866. /// </summary>
  867. /// <param name="msgBytes">Message bytes</param>
  868. private void Send(byte[] msgBytes)
  869. {
  870. try
  871. {
  872. // send message
  873. this.channel.Send(msgBytes);
  874. #if !BROKER
  875. // update last message sent ticks
  876. this.lastCommTime = Environment.TickCount;
  877. #endif
  878. }
  879. catch (Exception e)
  880. {
  881. #if TRACE
  882. MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
  883. #endif
  884. throw new MqttCommunicationException(e);
  885. }
  886. }
  887. /// <summary>
  888. /// Send a message
  889. /// </summary>
  890. /// <param name="msg">Message</param>
  891. private void Send(MqttMsgBase msg)
  892. {
  893. #if TRACE
  894. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg);
  895. #endif
  896. this.Send(msg.GetBytes((byte)this.ProtocolVersion));
  897. }
  898. /// <summary>
  899. /// Send a message to the broker and wait answer
  900. /// </summary>
  901. /// <param name="msgBytes">Message bytes</param>
  902. /// <returns>MQTT message response</returns>
  903. private MqttMsgBase SendReceive(byte[] msgBytes)
  904. {
  905. return this.SendReceive(msgBytes, MqttSettings.MQTT_DEFAULT_TIMEOUT);
  906. }
  907. /// <summary>
  908. /// Send a message to the broker and wait answer
  909. /// </summary>
  910. /// <param name="msgBytes">Message bytes</param>
  911. /// <param name="timeout">Timeout for receiving answer</param>
  912. /// <returns>MQTT message response</returns>
  913. private MqttMsgBase SendReceive(byte[] msgBytes, int timeout)
  914. {
  915. // reset handle before sending
  916. this.syncEndReceiving.Reset();
  917. try
  918. {
  919. // send message
  920. this.channel.Send(msgBytes);
  921. // update last message sent ticks
  922. this.lastCommTime = Environment.TickCount;
  923. }
  924. catch (Exception e)
  925. {
  926. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
  927. if (typeof(SocketException) == e.GetType())
  928. {
  929. // connection reset by broker
  930. if (((SocketException)e).SocketErrorCode == SocketError.ConnectionReset)
  931. this.IsConnected = false;
  932. }
  933. #endif
  934. #if TRACE
  935. MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
  936. #endif
  937. throw new MqttCommunicationException(e);
  938. }
  939. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  940. // wait for answer from broker
  941. if (this.syncEndReceiving.WaitOne(timeout, false))
  942. #else
  943. // wait for answer from broker
  944. if (this.syncEndReceiving.WaitOne(timeout))
  945. #endif
  946. {
  947. // message received without exception
  948. if (this.exReceiving == null)
  949. return this.msgReceived;
  950. // receiving thread catched exception
  951. else
  952. throw this.exReceiving;
  953. }
  954. else
  955. {
  956. // throw timeout exception
  957. throw new MqttCommunicationException();
  958. }
  959. }
  960. /// <summary>
  961. /// Send a message to the broker and wait answer
  962. /// </summary>
  963. /// <param name="msg">Message</param>
  964. /// <returns>MQTT message response</returns>
  965. private MqttMsgBase SendReceive(MqttMsgBase msg)
  966. {
  967. return this.SendReceive(msg, MqttSettings.MQTT_DEFAULT_TIMEOUT);
  968. }
  969. /// <summary>
  970. /// Send a message to the broker and wait answer
  971. /// </summary>
  972. /// <param name="msg">Message</param>
  973. /// <param name="timeout">Timeout for receiving answer</param>
  974. /// <returns>MQTT message response</returns>
  975. private MqttMsgBase SendReceive(MqttMsgBase msg, int timeout)
  976. {
  977. #if TRACE
  978. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg);
  979. #endif
  980. return this.SendReceive(msg.GetBytes((byte)this.ProtocolVersion), timeout);
  981. }
  982. /// <summary>
  983. /// Enqueue a message into the inflight queue
  984. /// </summary>
  985. /// <param name="msg">Message to enqueue</param>
  986. /// <param name="flow">Message flow (publish, acknowledge)</param>
  987. /// <returns>Message enqueued or not</returns>
  988. private bool EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow)
  989. {
  990. // enqueue is needed (or not)
  991. bool enqueue = true;
  992. // if it is a PUBLISH message with QoS Level 2
  993. if ((msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  994. (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
  995. {
  996. lock (this.inflightQueue)
  997. {
  998. // if it is a PUBLISH message already received (it is in the inflight queue), the publisher
  999. // re-sent it because it didn't received the PUBREC. In this case, we have to re-send PUBREC
  1000. // NOTE : I need to find on message id and flow because the broker could be publish/received
  1001. // to/from client and message id could be the same (one tracked by broker and the other by client)
  1002. MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge);
  1003. MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);
  1004. // the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need
  1005. // to change state to re-send PUBREC
  1006. if (msgCtx != null)
  1007. {
  1008. msgCtx.State = MqttMsgState.QueuedQos2;
  1009. msgCtx.Flow = MqttMsgFlow.ToAcknowledge;
  1010. enqueue = false;
  1011. }
  1012. }
  1013. }
  1014. if (enqueue)
  1015. {
  1016. // set a default state
  1017. MqttMsgState state = MqttMsgState.QueuedQos0;
  1018. // based on QoS level, the messages flow between broker and client changes
  1019. switch (msg.QosLevel)
  1020. {
  1021. // QoS Level 0
  1022. case MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE:
  1023. state = MqttMsgState.QueuedQos0;
  1024. break;
  1025. // QoS Level 1
  1026. case MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE:
  1027. state = MqttMsgState.QueuedQos1;
  1028. break;
  1029. // QoS Level 2
  1030. case MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE:
  1031. state = MqttMsgState.QueuedQos2;
  1032. break;
  1033. }
  1034. // [v3.1.1] SUBSCRIBE and UNSUBSCRIBE aren't "officially" QOS = 1
  1035. // so QueuedQos1 state isn't valid for them
  1036. if (msg.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
  1037. state = MqttMsgState.SendSubscribe;
  1038. else if (msg.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE)
  1039. state = MqttMsgState.SendUnsubscribe;
  1040. // queue message context
  1041. MqttMsgContext msgContext = new MqttMsgContext()
  1042. {
  1043. Message = msg,
  1044. State = state,
  1045. Flow = flow,
  1046. Attempt = 0
  1047. };
  1048. lock (this.inflightQueue)
  1049. {
  1050. // check number of messages inside inflight queue
  1051. enqueue = (this.inflightQueue.Count < this.settings.InflightQueueSize);
  1052. if (enqueue)
  1053. {
  1054. // enqueue message and unlock send thread
  1055. this.inflightQueue.Enqueue(msgContext);
  1056. #if TRACE
  1057. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg);
  1058. #endif
  1059. // PUBLISH message
  1060. if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
  1061. {
  1062. // to publish and QoS level 1 or 2
  1063. if ((msgContext.Flow == MqttMsgFlow.ToPublish) &&
  1064. ((msg.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) ||
  1065. (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)))
  1066. {
  1067. if (this.session != null)
  1068. this.session.InflightMessages.Add(msgContext.Key, msgContext);
  1069. }
  1070. // to acknowledge and QoS level 2
  1071. else if ((msgContext.Flow == MqttMsgFlow.ToAcknowledge) &&
  1072. (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
  1073. {
  1074. if (this.session != null)
  1075. this.session.InflightMessages.Add(msgContext.Key, msgContext);
  1076. }
  1077. }
  1078. }
  1079. }
  1080. }
  1081. this.inflightWaitHandle.Set();
  1082. return enqueue;
  1083. }
  1084. /// <summary>
  1085. /// Enqueue a message into the internal queue
  1086. /// </summary>
  1087. /// <param name="msg">Message to enqueue</param>
  1088. private void EnqueueInternal(MqttMsgBase msg)
  1089. {
  1090. // enqueue is needed (or not)
  1091. bool enqueue = true;
  1092. // if it is a PUBREL message (for QoS Level 2)
  1093. if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE)
  1094. {
  1095. lock (this.inflightQueue)
  1096. {
  1097. // if it is a PUBREL but the corresponding PUBLISH isn't in the inflight queue,
  1098. // it means that we processed PUBLISH message and received PUBREL and we sent PUBCOMP
  1099. // but publisher didn't receive PUBCOMP so it re-sent PUBREL. We need only to re-send PUBCOMP.
  1100. // NOTE : I need to find on message id and flow because the broker could be publish/received
  1101. // to/from client and message id could be the same (one tracked by broker and the other by client)
  1102. MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge);
  1103. MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);
  1104. // the PUBLISH message isn't in the inflight queue, it was already processed so
  1105. // we need to re-send PUBCOMP only
  1106. if (msgCtx == null)
  1107. {
  1108. MqttMsgPubcomp pubcomp = new MqttMsgPubcomp();
  1109. pubcomp.MessageId = msg.MessageId;
  1110. this.Send(pubcomp);
  1111. enqueue = false;
  1112. }
  1113. }
  1114. }
  1115. // if it is a PUBCOMP message (for QoS Level 2)
  1116. else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE)
  1117. {
  1118. lock (this.inflightQueue)
  1119. {
  1120. // if it is a PUBCOMP but the corresponding PUBLISH isn't in the inflight queue,
  1121. // it means that we sent PUBLISH message, sent PUBREL (after receiving PUBREC) and already received PUBCOMP
  1122. // but publisher didn't receive PUBREL so it re-sent PUBCOMP. We need only to ignore this PUBCOMP.
  1123. // NOTE : I need to find on message id and flow because the broker could be publish/received
  1124. // to/from client and message id could be the same (one tracked by broker and the other by client)
  1125. MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
  1126. MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);
  1127. // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBCOMP
  1128. if (msgCtx == null)
  1129. {
  1130. enqueue = false;
  1131. }
  1132. }
  1133. }
  1134. // if it is a PUBREC message (for QoS Level 2)
  1135. else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)
  1136. {
  1137. lock (this.inflightQueue)
  1138. {
  1139. // if it is a PUBREC but the corresponding PUBLISH isn't in the inflight queue,
  1140. // it means that we sent PUBLISH message more times (retries) but broker didn't send PUBREC in time
  1141. // the publish is failed and we need only to ignore this PUBREC.
  1142. // NOTE : I need to find on message id and flow because the broker could be publish/received
  1143. // to/from client and message id could be the same (one tracked by broker and the other by client)
  1144. MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
  1145. MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);
  1146. // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBREC
  1147. if (msgCtx == null)
  1148. {
  1149. enqueue = false;
  1150. }
  1151. }
  1152. }
  1153. if (enqueue)
  1154. {
  1155. lock (this.internalQueue)
  1156. {
  1157. this.internalQueue.Enqueue(msg);
  1158. #if TRACE
  1159. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg);
  1160. #endif
  1161. this.inflightWaitHandle.Set();
  1162. }
  1163. }
  1164. }
  1165. /// <summary>
  1166. /// Thread for receiving messages
  1167. /// </summary>
  1168. private void ReceiveThread()
  1169. {
  1170. int readBytes = 0;
  1171. byte[] fixedHeaderFirstByte = new byte[1];
  1172. byte msgType;
  1173. while (this.isRunning)
  1174. {
  1175. try
  1176. {
  1177. // read first byte (fixed header)
  1178. readBytes = this.channel.Receive(fixedHeaderFirstByte);
  1179. if (readBytes > 0)
  1180. {
  1181. #if BROKER
  1182. // update last message received ticks
  1183. this.lastCommTime = Environment.TickCount;
  1184. #endif
  1185. // extract message type from received byte
  1186. msgType = (byte)((fixedHeaderFirstByte[0] & MqttMsgBase.MSG_TYPE_MASK) >> MqttMsgBase.MSG_TYPE_OFFSET);
  1187. switch (msgType)
  1188. {
  1189. // CONNECT message received
  1190. case MqttMsgBase.MQTT_MSG_CONNECT_TYPE:
  1191. #if BROKER
  1192. MqttMsgConnect connect = MqttMsgConnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1193. #if TRACE
  1194. Trace.WriteLine(TraceLevel.Frame, "RECV {0}", connect);
  1195. #endif
  1196. // raise message received event
  1197. this.OnInternalEvent(new MsgInternalEvent(connect));
  1198. break;
  1199. #else
  1200. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1201. #endif
  1202. // CONNACK message received
  1203. case MqttMsgBase.MQTT_MSG_CONNACK_TYPE:
  1204. #if BROKER
  1205. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1206. #else
  1207. this.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1208. #if TRACE
  1209. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
  1210. #endif
  1211. this.syncEndReceiving.Set();
  1212. break;
  1213. #endif
  1214. // PINGREQ message received
  1215. case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE:
  1216. #if BROKER
  1217. this.msgReceived = MqttMsgPingReq.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1218. #if TRACE
  1219. Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
  1220. #endif
  1221. MqttMsgPingResp pingresp = new MqttMsgPingResp();
  1222. this.Send(pingresp);
  1223. break;
  1224. #else
  1225. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1226. #endif
  1227. // PINGRESP message received
  1228. case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE:
  1229. #if BROKER
  1230. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1231. #else
  1232. this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1233. #if TRACE
  1234. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
  1235. #endif
  1236. this.syncEndReceiving.Set();
  1237. break;
  1238. #endif
  1239. // SUBSCRIBE message received
  1240. case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
  1241. #if BROKER
  1242. MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1243. #if TRACE
  1244. Trace.WriteLine(TraceLevel.Frame, "RECV {0}", subscribe);
  1245. #endif
  1246. // raise message received event
  1247. this.OnInternalEvent(new MsgInternalEvent(subscribe));
  1248. break;
  1249. #else
  1250. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1251. #endif
  1252. // SUBACK message received
  1253. case MqttMsgBase.MQTT_MSG_SUBACK_TYPE:
  1254. #if BROKER
  1255. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1256. #else
  1257. // enqueue SUBACK message received (for QoS Level 1) into the internal queue
  1258. MqttMsgSuback suback = MqttMsgSuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1259. #if TRACE
  1260. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", suback);
  1261. #endif
  1262. // enqueue SUBACK message into the internal queue
  1263. this.EnqueueInternal(suback);
  1264. break;
  1265. #endif
  1266. // PUBLISH message received
  1267. case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
  1268. MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1269. #if TRACE
  1270. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", publish);
  1271. #endif
  1272. // enqueue PUBLISH message to acknowledge into the inflight queue
  1273. this.EnqueueInflight(publish, MqttMsgFlow.ToAcknowledge);
  1274. break;
  1275. // PUBACK message received
  1276. case MqttMsgBase.MQTT_MSG_PUBACK_TYPE:
  1277. // enqueue PUBACK message received (for QoS Level 1) into the internal queue
  1278. MqttMsgPuback puback = MqttMsgPuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1279. #if TRACE
  1280. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", puback);
  1281. #endif
  1282. // enqueue PUBACK message into the internal queue
  1283. this.EnqueueInternal(puback);
  1284. break;
  1285. // PUBREC message received
  1286. case MqttMsgBase.MQTT_MSG_PUBREC_TYPE:
  1287. // enqueue PUBREC message received (for QoS Level 2) into the internal queue
  1288. MqttMsgPubrec pubrec = MqttMsgPubrec.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1289. #if TRACE
  1290. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrec);
  1291. #endif
  1292. // enqueue PUBREC message into the internal queue
  1293. this.EnqueueInternal(pubrec);
  1294. break;
  1295. // PUBREL message received
  1296. case MqttMsgBase.MQTT_MSG_PUBREL_TYPE:
  1297. // enqueue PUBREL message received (for QoS Level 2) into the internal queue
  1298. MqttMsgPubrel pubrel = MqttMsgPubrel.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1299. #if TRACE
  1300. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrel);
  1301. #endif
  1302. // enqueue PUBREL message into the internal queue
  1303. this.EnqueueInternal(pubrel);
  1304. break;
  1305. // PUBCOMP message received
  1306. case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE:
  1307. // enqueue PUBCOMP message received (for QoS Level 2) into the internal queue
  1308. MqttMsgPubcomp pubcomp = MqttMsgPubcomp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1309. #if TRACE
  1310. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubcomp);
  1311. #endif
  1312. // enqueue PUBCOMP message into the internal queue
  1313. this.EnqueueInternal(pubcomp);
  1314. break;
  1315. // UNSUBSCRIBE message received
  1316. case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE:
  1317. #if BROKER
  1318. MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1319. #if TRACE
  1320. Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsubscribe);
  1321. #endif
  1322. // raise message received event
  1323. this.OnInternalEvent(new MsgInternalEvent(unsubscribe));
  1324. break;
  1325. #else
  1326. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1327. #endif
  1328. // UNSUBACK message received
  1329. case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE:
  1330. #if BROKER
  1331. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1332. #else
  1333. // enqueue UNSUBACK message received (for QoS Level 1) into the internal queue
  1334. MqttMsgUnsuback unsuback = MqttMsgUnsuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1335. #if TRACE
  1336. MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsuback);
  1337. #endif
  1338. // enqueue UNSUBACK message into the internal queue
  1339. this.EnqueueInternal(unsuback);
  1340. break;
  1341. #endif
  1342. // DISCONNECT message received
  1343. case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE:
  1344. #if BROKER
  1345. MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
  1346. #if TRACE
  1347. Trace.WriteLine(TraceLevel.Frame, "RECV {0}", disconnect);
  1348. #endif
  1349. // raise message received event
  1350. this.OnInternalEvent(new MsgInternalEvent(disconnect));
  1351. break;
  1352. #else
  1353. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1354. #endif
  1355. default:
  1356. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1357. }
  1358. this.exReceiving = null;
  1359. }
  1360. // zero bytes read, peer gracefully closed socket
  1361. else
  1362. {
  1363. // wake up thread that will notify connection is closing
  1364. this.OnConnectionClosing();
  1365. }
  1366. }
  1367. catch (Exception e)
  1368. {
  1369. #if TRACE
  1370. MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
  1371. #endif
  1372. this.exReceiving = new MqttCommunicationException(e);
  1373. bool close = false;
  1374. if (e.GetType() == typeof(MqttClientException))
  1375. {
  1376. // [v3.1.1] scenarios the receiver MUST close the network connection
  1377. MqttClientException ex = e as MqttClientException;
  1378. close = ((ex.ErrorCode == MqttClientErrorCode.InvalidFlagBits) ||
  1379. (ex.ErrorCode == MqttClientErrorCode.InvalidProtocolName) ||
  1380. (ex.ErrorCode == MqttClientErrorCode.InvalidConnectFlags));
  1381. }
  1382. #if !(WINDOWS_APP || WINDOWS_PHONE_APP)
  1383. else if ((e.GetType() == typeof(SocketException)) ||
  1384. ((e.InnerException != null) && (e.InnerException.GetType() == typeof(SocketException)))) // added for SSL/TLS incoming connection that use SslStream that wraps SocketException
  1385. {
  1386. close = true;
  1387. }
  1388. #endif
  1389. if (close)
  1390. {
  1391. // wake up thread that will notify connection is closing
  1392. this.OnConnectionClosing();
  1393. }
  1394. }
  1395. }
  1396. }
  1397. /// <summary>
  1398. /// Thread for handling keep alive message
  1399. /// </summary>
  1400. private void KeepAliveThread()
  1401. {
  1402. int delta = 0;
  1403. int wait = this.keepAlivePeriod;
  1404. // create event to signal that current thread is end
  1405. this.keepAliveEventEnd = new AutoResetEvent(false);
  1406. while (this.isRunning)
  1407. {
  1408. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1409. // waiting...
  1410. this.keepAliveEvent.WaitOne(wait, false);
  1411. #else
  1412. // waiting...
  1413. this.keepAliveEvent.WaitOne(wait);
  1414. #endif
  1415. if (this.isRunning)
  1416. {
  1417. delta = Environment.TickCount - this.lastCommTime;
  1418. // if timeout exceeded ...
  1419. if (delta >= this.keepAlivePeriod)
  1420. {
  1421. #if BROKER
  1422. // client must close connection
  1423. this.OnConnectionClosing();
  1424. #else
  1425. // ... send keep alive
  1426. this.Ping();
  1427. wait = this.keepAlivePeriod;
  1428. #endif
  1429. }
  1430. else
  1431. {
  1432. // update waiting time
  1433. wait = this.keepAlivePeriod - delta;
  1434. }
  1435. }
  1436. }
  1437. // signal thread end
  1438. this.keepAliveEventEnd.Set();
  1439. }
  1440. /// <summary>
  1441. /// Thread for raising event
  1442. /// </summary>
  1443. private void DispatchEventThread()
  1444. {
  1445. while (this.isRunning)
  1446. {
  1447. #if BROKER
  1448. if ((this.eventQueue.Count == 0) && !this.isConnectionClosing)
  1449. {
  1450. // broker need to receive the first message (CONNECT)
  1451. // within a reasonable amount of time after TCP/IP connection
  1452. if (!this.IsConnected)
  1453. {
  1454. // wait on receiving message from client with a connection timeout
  1455. if (!this.receiveEventWaitHandle.WaitOne(this.settings.TimeoutOnConnection))
  1456. {
  1457. // client must close connection
  1458. this.Close();
  1459. // client raw disconnection
  1460. this.OnConnectionClosed();
  1461. }
  1462. }
  1463. else
  1464. {
  1465. // wait on receiving message from client
  1466. this.receiveEventWaitHandle.WaitOne();
  1467. }
  1468. }
  1469. #else
  1470. if ((this.eventQueue.Count == 0) && !this.isConnectionClosing)
  1471. // wait on receiving message from client
  1472. this.receiveEventWaitHandle.WaitOne();
  1473. #endif
  1474. // check if it is running or we are closing client
  1475. if (this.isRunning)
  1476. {
  1477. // get event from queue
  1478. InternalEvent internalEvent = null;
  1479. lock (this.eventQueue)
  1480. {
  1481. if (this.eventQueue.Count > 0)
  1482. internalEvent = (InternalEvent)this.eventQueue.Dequeue();
  1483. }
  1484. // it's an event with a message inside
  1485. if (internalEvent != null)
  1486. {
  1487. MqttMsgBase msg = ((MsgInternalEvent)internalEvent).Message;
  1488. if (msg != null)
  1489. {
  1490. switch (msg.Type)
  1491. {
  1492. // CONNECT message received
  1493. case MqttMsgBase.MQTT_MSG_CONNECT_TYPE:
  1494. #if BROKER
  1495. // raise connected client event (CONNECT message received)
  1496. this.OnMqttMsgConnected((MqttMsgConnect)msg);
  1497. break;
  1498. #else
  1499. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1500. #endif
  1501. // SUBSCRIBE message received
  1502. case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
  1503. #if BROKER
  1504. MqttMsgSubscribe subscribe = (MqttMsgSubscribe)msg;
  1505. // raise subscribe topic event (SUBSCRIBE message received)
  1506. this.OnMqttMsgSubscribeReceived(subscribe.MessageId, subscribe.Topics, subscribe.QoSLevels);
  1507. break;
  1508. #else
  1509. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1510. #endif
  1511. // SUBACK message received
  1512. case MqttMsgBase.MQTT_MSG_SUBACK_TYPE:
  1513. // raise subscribed topic event (SUBACK message received)
  1514. this.OnMqttMsgSubscribed((MqttMsgSuback)msg);
  1515. break;
  1516. // PUBLISH message received
  1517. case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
  1518. // PUBLISH message received in a published internal event, no publish succeeded
  1519. if (internalEvent.GetType() == typeof(MsgPublishedInternalEvent))
  1520. this.OnMqttMsgPublished(msg.MessageId, false);
  1521. else
  1522. // raise PUBLISH message received event
  1523. this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
  1524. break;
  1525. // PUBACK message received
  1526. case MqttMsgBase.MQTT_MSG_PUBACK_TYPE:
  1527. // raise published message event
  1528. // (PUBACK received for QoS Level 1)
  1529. this.OnMqttMsgPublished(msg.MessageId, true);
  1530. break;
  1531. // PUBREL message received
  1532. case MqttMsgBase.MQTT_MSG_PUBREL_TYPE:
  1533. // raise message received event
  1534. // (PUBREL received for QoS Level 2)
  1535. this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
  1536. break;
  1537. // PUBCOMP message received
  1538. case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE:
  1539. // raise published message event
  1540. // (PUBCOMP received for QoS Level 2)
  1541. this.OnMqttMsgPublished(msg.MessageId, true);
  1542. break;
  1543. // UNSUBSCRIBE message received from client
  1544. case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE:
  1545. #if BROKER
  1546. MqttMsgUnsubscribe unsubscribe = (MqttMsgUnsubscribe)msg;
  1547. // raise unsubscribe topic event (UNSUBSCRIBE message received)
  1548. this.OnMqttMsgUnsubscribeReceived(unsubscribe.MessageId, unsubscribe.Topics);
  1549. break;
  1550. #else
  1551. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1552. #endif
  1553. // UNSUBACK message received
  1554. case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE:
  1555. // raise unsubscribed topic event
  1556. this.OnMqttMsgUnsubscribed(msg.MessageId);
  1557. break;
  1558. // DISCONNECT message received from client
  1559. case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE:
  1560. #if BROKER
  1561. // raise disconnected client event (DISCONNECT message received)
  1562. this.OnMqttMsgDisconnected();
  1563. break;
  1564. #else
  1565. throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
  1566. #endif
  1567. }
  1568. }
  1569. }
  1570. // all events for received messages dispatched, check if there is closing connection
  1571. if ((this.eventQueue.Count == 0) && this.isConnectionClosing)
  1572. {
  1573. // client must close connection
  1574. this.Close();
  1575. // client raw disconnection
  1576. this.OnConnectionClosed();
  1577. }
  1578. }
  1579. }
  1580. }
  1581. /// <summary>
  1582. /// Process inflight messages queue
  1583. /// </summary>
  1584. private void ProcessInflightThread()
  1585. {
  1586. MqttMsgContext msgContext = null;
  1587. MqttMsgBase msgInflight = null;
  1588. MqttMsgBase msgReceived = null;
  1589. InternalEvent internalEvent = null;
  1590. bool acknowledge = false;
  1591. int timeout = Timeout.Infinite;
  1592. int delta;
  1593. bool msgReceivedProcessed = false;
  1594. try
  1595. {
  1596. while (this.isRunning)
  1597. {
  1598. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1599. // wait on message queueud to inflight
  1600. this.inflightWaitHandle.WaitOne(timeout, false);
  1601. #else
  1602. // wait on message queueud to inflight
  1603. this.inflightWaitHandle.WaitOne(timeout);
  1604. #endif
  1605. // it could be unblocked because Close() method is joining
  1606. if (this.isRunning)
  1607. {
  1608. lock (this.inflightQueue)
  1609. {
  1610. // message received and peeked from internal queue is processed
  1611. // NOTE : it has the corresponding message in inflight queue based on messageId
  1612. // (ex. a PUBREC for a PUBLISH, a SUBACK for a SUBSCRIBE, ...)
  1613. // if it's orphan we need to remove from internal queue
  1614. msgReceivedProcessed = false;
  1615. acknowledge = false;
  1616. msgReceived = null;
  1617. // set timeout tu MaxValue instead of Infinte (-1) to perform
  1618. // compare with calcultad current msgTimeout
  1619. timeout = Int32.MaxValue;
  1620. // a message inflight could be re-enqueued but we have to
  1621. // analyze it only just one time for cycle
  1622. int count = this.inflightQueue.Count;
  1623. // process all inflight queued messages
  1624. while (count > 0)
  1625. {
  1626. count--;
  1627. acknowledge = false;
  1628. msgReceived = null;
  1629. // check to be sure that client isn't closing and all queues are now empty !
  1630. if (!this.isRunning)
  1631. break;
  1632. // dequeue message context from queue
  1633. msgContext = (MqttMsgContext)this.inflightQueue.Dequeue();
  1634. // get inflight message
  1635. msgInflight = (MqttMsgBase)msgContext.Message;
  1636. switch (msgContext.State)
  1637. {
  1638. case MqttMsgState.QueuedQos0:
  1639. // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge
  1640. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1641. {
  1642. this.Send(msgInflight);
  1643. }
  1644. // QoS 0, no need acknowledge
  1645. else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
  1646. {
  1647. internalEvent = new MsgInternalEvent(msgInflight);
  1648. // notify published message from broker (no need acknowledged)
  1649. this.OnInternalEvent(internalEvent);
  1650. }
  1651. #if TRACE
  1652. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
  1653. #endif
  1654. break;
  1655. case MqttMsgState.QueuedQos1:
  1656. // [v3.1.1] SUBSCRIBE and UNSIBSCRIBE aren't "officially" QOS = 1
  1657. case MqttMsgState.SendSubscribe:
  1658. case MqttMsgState.SendUnsubscribe:
  1659. // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK
  1660. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1661. {
  1662. msgContext.Timestamp = Environment.TickCount;
  1663. msgContext.Attempt++;
  1664. if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
  1665. {
  1666. // PUBLISH message to send, wait for PUBACK
  1667. msgContext.State = MqttMsgState.WaitForPuback;
  1668. // retry ? set dup flag [v3.1.1] only for PUBLISH message
  1669. if (msgContext.Attempt > 1)
  1670. msgInflight.DupFlag = true;
  1671. }
  1672. else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
  1673. // SUBSCRIBE message to send, wait for SUBACK
  1674. msgContext.State = MqttMsgState.WaitForSuback;
  1675. else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE)
  1676. // UNSUBSCRIBE message to send, wait for UNSUBACK
  1677. msgContext.State = MqttMsgState.WaitForUnsuback;
  1678. this.Send(msgInflight);
  1679. // update timeout : minimum between delay (based on current message sent) or current timeout
  1680. timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
  1681. // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK)
  1682. this.inflightQueue.Enqueue(msgContext);
  1683. }
  1684. // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK
  1685. else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
  1686. {
  1687. MqttMsgPuback puback = new MqttMsgPuback();
  1688. puback.MessageId = msgInflight.MessageId;
  1689. this.Send(puback);
  1690. internalEvent = new MsgInternalEvent(msgInflight);
  1691. // notify published message from broker and acknowledged
  1692. this.OnInternalEvent(internalEvent);
  1693. #if TRACE
  1694. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
  1695. #endif
  1696. }
  1697. break;
  1698. case MqttMsgState.QueuedQos2:
  1699. // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC
  1700. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1701. {
  1702. msgContext.Timestamp = Environment.TickCount;
  1703. msgContext.Attempt++;
  1704. msgContext.State = MqttMsgState.WaitForPubrec;
  1705. // retry ? set dup flag
  1706. if (msgContext.Attempt > 1)
  1707. msgInflight.DupFlag = true;
  1708. this.Send(msgInflight);
  1709. // update timeout : minimum between delay (based on current message sent) or current timeout
  1710. timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
  1711. // re-enqueue message (I have to re-analyze for receiving PUBREC)
  1712. this.inflightQueue.Enqueue(msgContext);
  1713. }
  1714. // QoS 2, PUBLISH message received from broker to acknowledge, send PUBREC, state change to wait PUBREL
  1715. else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
  1716. {
  1717. MqttMsgPubrec pubrec = new MqttMsgPubrec();
  1718. pubrec.MessageId = msgInflight.MessageId;
  1719. msgContext.State = MqttMsgState.WaitForPubrel;
  1720. this.Send(pubrec);
  1721. // re-enqueue message (I have to re-analyze for receiving PUBREL)
  1722. this.inflightQueue.Enqueue(msgContext);
  1723. }
  1724. break;
  1725. case MqttMsgState.WaitForPuback:
  1726. case MqttMsgState.WaitForSuback:
  1727. case MqttMsgState.WaitForUnsuback:
  1728. // QoS 1, waiting for PUBACK of a PUBLISH message sent or
  1729. // waiting for SUBACK of a SUBSCRIBE message sent or
  1730. // waiting for UNSUBACK of a UNSUBSCRIBE message sent or
  1731. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1732. {
  1733. acknowledge = false;
  1734. lock (this.internalQueue)
  1735. {
  1736. if (this.internalQueue.Count > 0)
  1737. msgReceived = (MqttMsgBase)this.internalQueue.Peek();
  1738. }
  1739. // it is a PUBACK message or a SUBACK/UNSUBACK message
  1740. if (msgReceived != null)
  1741. {
  1742. // PUBACK message or SUBACK/UNSUBACK message for the current message
  1743. if (((msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) ||
  1744. ((msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) ||
  1745. ((msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)))
  1746. {
  1747. lock (this.internalQueue)
  1748. {
  1749. // received message processed
  1750. this.internalQueue.Dequeue();
  1751. acknowledge = true;
  1752. msgReceivedProcessed = true;
  1753. #if TRACE
  1754. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
  1755. #endif
  1756. }
  1757. // if PUBACK received, confirm published with flag
  1758. if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE)
  1759. internalEvent = new MsgPublishedInternalEvent(msgReceived, true);
  1760. else
  1761. internalEvent = new MsgInternalEvent(msgReceived);
  1762. // notify received acknowledge from broker of a published message or subscribe/unsubscribe message
  1763. this.OnInternalEvent(internalEvent);
  1764. // PUBACK received for PUBLISH message with QoS Level 1, remove from session state
  1765. if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  1766. (this.session != null) &&
  1767. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1768. (this.session.InflightMessages.Contains(msgContext.Key)))
  1769. #else
  1770. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  1771. #endif
  1772. {
  1773. this.session.InflightMessages.Remove(msgContext.Key);
  1774. }
  1775. #if TRACE
  1776. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
  1777. #endif
  1778. }
  1779. }
  1780. // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid
  1781. if (!acknowledge)
  1782. {
  1783. delta = Environment.TickCount - msgContext.Timestamp;
  1784. // check timeout for receiving PUBACK since PUBLISH was sent or
  1785. // for receiving SUBACK since SUBSCRIBE was sent or
  1786. // for receiving UNSUBACK since UNSUBSCRIBE was sent
  1787. if (delta >= this.settings.DelayOnRetry)
  1788. {
  1789. // max retry not reached, resend
  1790. if (msgContext.Attempt < this.settings.AttemptsOnRetry)
  1791. {
  1792. msgContext.State = MqttMsgState.QueuedQos1;
  1793. // re-enqueue message
  1794. this.inflightQueue.Enqueue(msgContext);
  1795. // update timeout (0 -> reanalyze queue immediately)
  1796. timeout = 0;
  1797. }
  1798. else
  1799. {
  1800. // if PUBACK for a PUBLISH message not received after retries, raise event for not published
  1801. if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
  1802. {
  1803. // PUBACK not received in time, PUBLISH retries failed, need to remove from session inflight messages too
  1804. if ((this.session != null) &&
  1805. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1806. (this.session.InflightMessages.Contains(msgContext.Key)))
  1807. #else
  1808. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  1809. #endif
  1810. {
  1811. this.session.InflightMessages.Remove(msgContext.Key);
  1812. }
  1813. internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
  1814. // notify not received acknowledge from broker and message not published
  1815. this.OnInternalEvent(internalEvent);
  1816. }
  1817. // NOTE : not raise events for SUBACK or UNSUBACK not received
  1818. // for the user no event raised means subscribe/unsubscribe failed
  1819. }
  1820. }
  1821. else
  1822. {
  1823. // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK)
  1824. this.inflightQueue.Enqueue(msgContext);
  1825. // update timeout
  1826. int msgTimeout = (this.settings.DelayOnRetry - delta);
  1827. timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
  1828. }
  1829. }
  1830. }
  1831. break;
  1832. case MqttMsgState.WaitForPubrec:
  1833. // QoS 2, waiting for PUBREC of a PUBLISH message sent
  1834. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1835. {
  1836. acknowledge = false;
  1837. lock (this.internalQueue)
  1838. {
  1839. if (this.internalQueue.Count > 0)
  1840. msgReceived = (MqttMsgBase)this.internalQueue.Peek();
  1841. }
  1842. // it is a PUBREC message
  1843. if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE))
  1844. {
  1845. // PUBREC message for the current PUBLISH message, send PUBREL, wait for PUBCOMP
  1846. if (msgReceived.MessageId == msgInflight.MessageId)
  1847. {
  1848. lock (this.internalQueue)
  1849. {
  1850. // received message processed
  1851. this.internalQueue.Dequeue();
  1852. acknowledge = true;
  1853. msgReceivedProcessed = true;
  1854. #if TRACE
  1855. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
  1856. #endif
  1857. }
  1858. MqttMsgPubrel pubrel = new MqttMsgPubrel();
  1859. pubrel.MessageId = msgInflight.MessageId;
  1860. msgContext.State = MqttMsgState.WaitForPubcomp;
  1861. msgContext.Timestamp = Environment.TickCount;
  1862. msgContext.Attempt = 1;
  1863. this.Send(pubrel);
  1864. // update timeout : minimum between delay (based on current message sent) or current timeout
  1865. timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
  1866. // re-enqueue message
  1867. this.inflightQueue.Enqueue(msgContext);
  1868. }
  1869. }
  1870. // current message not acknowledged
  1871. if (!acknowledge)
  1872. {
  1873. delta = Environment.TickCount - msgContext.Timestamp;
  1874. // check timeout for receiving PUBREC since PUBLISH was sent
  1875. if (delta >= this.settings.DelayOnRetry)
  1876. {
  1877. // max retry not reached, resend
  1878. if (msgContext.Attempt < this.settings.AttemptsOnRetry)
  1879. {
  1880. msgContext.State = MqttMsgState.QueuedQos2;
  1881. // re-enqueue message
  1882. this.inflightQueue.Enqueue(msgContext);
  1883. // update timeout (0 -> reanalyze queue immediately)
  1884. timeout = 0;
  1885. }
  1886. else
  1887. {
  1888. // PUBREC not received in time, PUBLISH retries failed, need to remove from session inflight messages too
  1889. if ((this.session != null) &&
  1890. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1891. (this.session.InflightMessages.Contains(msgContext.Key)))
  1892. #else
  1893. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  1894. #endif
  1895. {
  1896. this.session.InflightMessages.Remove(msgContext.Key);
  1897. }
  1898. // if PUBREC for a PUBLISH message not received after retries, raise event for not published
  1899. internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
  1900. // notify not received acknowledge from broker and message not published
  1901. this.OnInternalEvent(internalEvent);
  1902. }
  1903. }
  1904. else
  1905. {
  1906. // re-enqueue message
  1907. this.inflightQueue.Enqueue(msgContext);
  1908. // update timeout
  1909. int msgTimeout = (this.settings.DelayOnRetry - delta);
  1910. timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
  1911. }
  1912. }
  1913. }
  1914. break;
  1915. case MqttMsgState.WaitForPubrel:
  1916. // QoS 2, waiting for PUBREL of a PUBREC message sent
  1917. if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
  1918. {
  1919. lock (this.internalQueue)
  1920. {
  1921. if (this.internalQueue.Count > 0)
  1922. msgReceived = (MqttMsgBase)this.internalQueue.Peek();
  1923. }
  1924. // it is a PUBREL message
  1925. if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE))
  1926. {
  1927. // PUBREL message for the current message, send PUBCOMP
  1928. if (msgReceived.MessageId == msgInflight.MessageId)
  1929. {
  1930. lock (this.internalQueue)
  1931. {
  1932. // received message processed
  1933. this.internalQueue.Dequeue();
  1934. msgReceivedProcessed = true;
  1935. #if TRACE
  1936. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
  1937. #endif
  1938. }
  1939. MqttMsgPubcomp pubcomp = new MqttMsgPubcomp();
  1940. pubcomp.MessageId = msgInflight.MessageId;
  1941. this.Send(pubcomp);
  1942. internalEvent = new MsgInternalEvent(msgInflight);
  1943. // notify published message from broker and acknowledged
  1944. this.OnInternalEvent(internalEvent);
  1945. // PUBREL received (and PUBCOMP sent) for PUBLISH message with QoS Level 2, remove from session state
  1946. if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  1947. (this.session != null) &&
  1948. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  1949. (this.session.InflightMessages.Contains(msgContext.Key)))
  1950. #else
  1951. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  1952. #endif
  1953. {
  1954. this.session.InflightMessages.Remove(msgContext.Key);
  1955. }
  1956. #if TRACE
  1957. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
  1958. #endif
  1959. }
  1960. else
  1961. {
  1962. // re-enqueue message
  1963. this.inflightQueue.Enqueue(msgContext);
  1964. }
  1965. }
  1966. else
  1967. {
  1968. // re-enqueue message
  1969. this.inflightQueue.Enqueue(msgContext);
  1970. }
  1971. }
  1972. break;
  1973. case MqttMsgState.WaitForPubcomp:
  1974. // QoS 2, waiting for PUBCOMP of a PUBREL message sent
  1975. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  1976. {
  1977. acknowledge = false;
  1978. lock (this.internalQueue)
  1979. {
  1980. if (this.internalQueue.Count > 0)
  1981. msgReceived = (MqttMsgBase)this.internalQueue.Peek();
  1982. }
  1983. // it is a PUBCOMP message
  1984. if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE))
  1985. {
  1986. // PUBCOMP message for the current message
  1987. if (msgReceived.MessageId == msgInflight.MessageId)
  1988. {
  1989. lock (this.internalQueue)
  1990. {
  1991. // received message processed
  1992. this.internalQueue.Dequeue();
  1993. acknowledge = true;
  1994. msgReceivedProcessed = true;
  1995. #if TRACE
  1996. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
  1997. #endif
  1998. }
  1999. internalEvent = new MsgPublishedInternalEvent(msgReceived, true);
  2000. // notify received acknowledge from broker of a published message
  2001. this.OnInternalEvent(internalEvent);
  2002. // PUBCOMP received for PUBLISH message with QoS Level 2, remove from session state
  2003. if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  2004. (this.session != null) &&
  2005. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  2006. (this.session.InflightMessages.Contains(msgContext.Key)))
  2007. #else
  2008. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  2009. #endif
  2010. {
  2011. this.session.InflightMessages.Remove(msgContext.Key);
  2012. }
  2013. #if TRACE
  2014. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
  2015. #endif
  2016. }
  2017. }
  2018. // it is a PUBREC message
  2019. else if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE))
  2020. {
  2021. // another PUBREC message for the current message due to a retransmitted PUBLISH
  2022. // I'm in waiting for PUBCOMP, so I can discard this PUBREC
  2023. if (msgReceived.MessageId == msgInflight.MessageId)
  2024. {
  2025. lock (this.internalQueue)
  2026. {
  2027. // received message processed
  2028. this.internalQueue.Dequeue();
  2029. acknowledge = true;
  2030. msgReceivedProcessed = true;
  2031. #if TRACE
  2032. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
  2033. #endif
  2034. // re-enqueue message
  2035. this.inflightQueue.Enqueue(msgContext);
  2036. }
  2037. }
  2038. }
  2039. // current message not acknowledged
  2040. if (!acknowledge)
  2041. {
  2042. delta = Environment.TickCount - msgContext.Timestamp;
  2043. // check timeout for receiving PUBCOMP since PUBREL was sent
  2044. if (delta >= this.settings.DelayOnRetry)
  2045. {
  2046. // max retry not reached, resend
  2047. if (msgContext.Attempt < this.settings.AttemptsOnRetry)
  2048. {
  2049. msgContext.State = MqttMsgState.SendPubrel;
  2050. // re-enqueue message
  2051. this.inflightQueue.Enqueue(msgContext);
  2052. // update timeout (0 -> reanalyze queue immediately)
  2053. timeout = 0;
  2054. }
  2055. else
  2056. {
  2057. // PUBCOMP not received, PUBREL retries failed, need to remove from session inflight messages too
  2058. if ((this.session != null) &&
  2059. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  2060. (this.session.InflightMessages.Contains(msgContext.Key)))
  2061. #else
  2062. (this.session.InflightMessages.ContainsKey(msgContext.Key)))
  2063. #endif
  2064. {
  2065. this.session.InflightMessages.Remove(msgContext.Key);
  2066. }
  2067. // if PUBCOMP for a PUBLISH message not received after retries, raise event for not published
  2068. internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
  2069. // notify not received acknowledge from broker and message not published
  2070. this.OnInternalEvent(internalEvent);
  2071. }
  2072. }
  2073. else
  2074. {
  2075. // re-enqueue message
  2076. this.inflightQueue.Enqueue(msgContext);
  2077. // update timeout
  2078. int msgTimeout = (this.settings.DelayOnRetry - delta);
  2079. timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
  2080. }
  2081. }
  2082. }
  2083. break;
  2084. case MqttMsgState.SendPubrec:
  2085. // TODO : impossible ? --> QueuedQos2 ToAcknowledge
  2086. break;
  2087. case MqttMsgState.SendPubrel:
  2088. // QoS 2, PUBREL message to send to broker, state change to wait PUBCOMP
  2089. if (msgContext.Flow == MqttMsgFlow.ToPublish)
  2090. {
  2091. MqttMsgPubrel pubrel = new MqttMsgPubrel();
  2092. pubrel.MessageId = msgInflight.MessageId;
  2093. msgContext.State = MqttMsgState.WaitForPubcomp;
  2094. msgContext.Timestamp = Environment.TickCount;
  2095. msgContext.Attempt++;
  2096. // retry ? set dup flag [v3.1.1] no needed
  2097. if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1)
  2098. {
  2099. if (msgContext.Attempt > 1)
  2100. pubrel.DupFlag = true;
  2101. }
  2102. this.Send(pubrel);
  2103. // update timeout : minimum between delay (based on current message sent) or current timeout
  2104. timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
  2105. // re-enqueue message
  2106. this.inflightQueue.Enqueue(msgContext);
  2107. }
  2108. break;
  2109. case MqttMsgState.SendPubcomp:
  2110. // TODO : impossible ?
  2111. break;
  2112. case MqttMsgState.SendPuback:
  2113. // TODO : impossible ? --> QueuedQos1 ToAcknowledge
  2114. break;
  2115. default:
  2116. break;
  2117. }
  2118. }
  2119. // if calculated timeout is MaxValue, it means that must be Infinite (-1)
  2120. if (timeout == Int32.MaxValue)
  2121. timeout = Timeout.Infinite;
  2122. // if message received is orphan, no corresponding message in inflight queue
  2123. // based on messageId, we need to remove from the queue
  2124. if ((msgReceived != null) && !msgReceivedProcessed)
  2125. {
  2126. this.internalQueue.Dequeue();
  2127. #if TRACE
  2128. MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0} orphan", msgReceived);
  2129. #endif
  2130. }
  2131. }
  2132. }
  2133. }
  2134. }
  2135. catch (MqttCommunicationException e)
  2136. {
  2137. // possible exception on Send, I need to re-enqueue not sent message
  2138. if (msgContext != null)
  2139. // re-enqueue message
  2140. this.inflightQueue.Enqueue(msgContext);
  2141. #if TRACE
  2142. MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
  2143. #endif
  2144. // raise disconnection client event
  2145. this.OnConnectionClosing();
  2146. }
  2147. }
  2148. /// <summary>
  2149. /// Restore session
  2150. /// </summary>
  2151. private void RestoreSession()
  2152. {
  2153. // if not clean session
  2154. if (!this.CleanSession)
  2155. {
  2156. // there is a previous session
  2157. if (this.session != null)
  2158. {
  2159. lock (this.inflightQueue)
  2160. {
  2161. foreach (MqttMsgContext msgContext in this.session.InflightMessages.Values)
  2162. {
  2163. this.inflightQueue.Enqueue(msgContext);
  2164. // if it is a PUBLISH message to publish
  2165. if ((msgContext.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  2166. (msgContext.Flow == MqttMsgFlow.ToPublish))
  2167. {
  2168. // it's QoS 1 and we haven't received PUBACK
  2169. if ((msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) &&
  2170. (msgContext.State == MqttMsgState.WaitForPuback))
  2171. {
  2172. // we haven't received PUBACK, we need to resend PUBLISH message
  2173. msgContext.State = MqttMsgState.QueuedQos1;
  2174. }
  2175. // it's QoS 2
  2176. else if (msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)
  2177. {
  2178. // we haven't received PUBREC, we need to resend PUBLISH message
  2179. if (msgContext.State == MqttMsgState.WaitForPubrec)
  2180. {
  2181. msgContext.State = MqttMsgState.QueuedQos2;
  2182. }
  2183. // we haven't received PUBCOMP, we need to resend PUBREL for it
  2184. else if (msgContext.State == MqttMsgState.WaitForPubcomp)
  2185. {
  2186. msgContext.State = MqttMsgState.SendPubrel;
  2187. }
  2188. }
  2189. }
  2190. }
  2191. }
  2192. // unlock process inflight queue
  2193. this.inflightWaitHandle.Set();
  2194. }
  2195. else
  2196. {
  2197. // create new session
  2198. this.session = new MqttClientSession(this.ClientId);
  2199. }
  2200. }
  2201. // clean any previous session
  2202. else
  2203. {
  2204. if (this.session != null)
  2205. this.session.Clear();
  2206. }
  2207. }
  2208. #if BROKER
  2209. /// <summary>
  2210. /// Load a given session
  2211. /// </summary>
  2212. /// <param name="session">MQTT Client session to load</param>
  2213. public void LoadSession(MqttClientSession session)
  2214. {
  2215. // if not clean session
  2216. if (!this.CleanSession)
  2217. {
  2218. // set the session ...
  2219. this.session = session;
  2220. // ... and restore it
  2221. this.RestoreSession();
  2222. }
  2223. }
  2224. #endif
  2225. /// <summary>
  2226. /// Generate the next message identifier
  2227. /// </summary>
  2228. /// <returns>Message identifier</returns>
  2229. private ushort GetMessageId()
  2230. {
  2231. // if 0 or max UInt16, it becomes 1 (first valid messageId)
  2232. this.messageIdCounter = ((this.messageIdCounter % UInt16.MaxValue) != 0) ? (ushort)(this.messageIdCounter + 1) : (ushort)1;
  2233. return this.messageIdCounter;
  2234. }
  2235. /// <summary>
  2236. /// Finder class for PUBLISH message inside a queue
  2237. /// </summary>
  2238. internal class MqttMsgContextFinder
  2239. {
  2240. // PUBLISH message id
  2241. internal ushort MessageId { get; set; }
  2242. // message flow into inflight queue
  2243. internal MqttMsgFlow Flow { get; set; }
  2244. /// <summary>
  2245. /// Constructor
  2246. /// </summary>
  2247. /// <param name="messageId">Message Id</param>
  2248. /// <param name="flow">Message flow inside inflight queue</param>
  2249. internal MqttMsgContextFinder(ushort messageId, MqttMsgFlow flow)
  2250. {
  2251. this.MessageId = messageId;
  2252. this.Flow = flow;
  2253. }
  2254. internal bool Find(object item)
  2255. {
  2256. MqttMsgContext msgCtx = (MqttMsgContext)item;
  2257. return ((msgCtx.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
  2258. (msgCtx.Message.MessageId == this.MessageId) &&
  2259. msgCtx.Flow == this.Flow);
  2260. }
  2261. }
  2262. }
  2263. /// <summary>
  2264. /// MQTT protocol version
  2265. /// </summary>
  2266. public enum MqttProtocolVersion
  2267. {
  2268. Version_3_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1,
  2269. Version_3_1_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1_1
  2270. }
  2271. }