English Русский Español 日本語 Português
preview
Entwicklung eines MQTT-Clients für Metatrader 5: ein TDD-Ansatz — Teil 4

Entwicklung eines MQTT-Clients für Metatrader 5: ein TDD-Ansatz — Teil 4

MetaTrader 5Integration | 14 Februar 2024, 11:10
112 0
Jocimar Lopes
Jocimar Lopes

„Softwareeinheiten sollten offen für Erweiterungen, aber geschlossen für Änderungen sein.“ (Offen-geschlossen-Prinzip für objektorientierte Programmierung)

Einführung

Um sicher zu sein, dass wir alle auf derselben Seite stehen, könnte eine kurze Zusammenfassung hilfreich sein. Im ersten Teil dieser Serie haben wir begonnen, naive Tests für eine Funktion zu schreiben, die in der Lage ist, einen MQTT Control Packet Fixed Header zu erzeugen. Wir haben mit dem ersten Bit des ersten Bytes begonnen. Wenn es einen kleinen Schritt gibt, dann war das ganz sicher einer.

Im zweiten Teil haben wir einige gemeinsame Funktionen und Definitionen in zwei Header-Dateien organisiert.

Im dritten Teil beginnen wir mit dem Auslesen der Acknowledge-Flags und Connect Reason Codes von CONNACK. Es war unser erster Kontakt mit der Abteilung „Operatives Verhalten“ des Standards. 

Bis zu diesem Punkt war alles statisch und an einen Verbindungsversuch gebunden. Wenn das erste Byte falsch ist, erhält man eine Fehlermeldung des Brokers und kann erneut versuchen, eine Verbindung mit einem wohlgeformten Paket herzustellen. 

Eigenschaften sind eine andere Geschichte. Sie sind dynamische Attribute der MQTT Application Message und können sich nach der Verbindung ändern. Das maximale QoS eines Brokers kann sich aus betrieblichen Gründen vorübergehend ändern. Ein Empfangsmaximum (Receive Maximum) kann aufgrund von Netzwerkengpässen geändert worden sein. Darüber hinaus sind einige Eigenschaften von vornherein dynamisch, wie Content-Type, alle Will-Eigenschaften und die User-Eigenschaft. Diese Eigenschaften werden sich ständig ändern.

Der OASIS-Standard ist in seinen Spezifikationen ziemlich klar, aber es bleibt viel Spielraum für die Client-Entwickler, um zu entscheiden, wie die aktuellen Eigenschaften zu lesen, zu reagieren, beizubehalten und zu aktualisieren sind. Übrigens liegt die Persistenzschicht für die Verwaltung des Sitzungsstatus zu hundert Prozent in der Verantwortung des Client-Entwicklers. Wir werden eine Persistenzschicht implementieren müssen, um die Eigenschaften zwischen den Sitzungen ordnungsgemäß zu verwalten. Die Wahl unserer Algorithmen ist hier entscheidend für Konformität, Robustheit und Leistung.

Für zukünftige Bibliotheksverwalter können die Kommentare im Abschnitt „Wie wir MQTT v5.0 Properties in MQL5 lesen“ als informelle Dokumentation von Nutzen sein. Auch einige Endnutzer von Bibliotheken können von diesen Kommentaren profitieren. In diesem Abschnitt werden wir die Eigenschaften aus der Sicht eines Bibliotheksentwicklers betrachten. Wir werden uns mit ihren Datentypen, Bezeichnern und ihrer Position im Byte-Array befassen, um besser zu beschreiben, wie wir sie lesen. Im Folgenden werden wir die Eigenschaften aus der Sicht eines Bibliotheksnutzers betrachten. Wir werden versuchen, ihre Semantik in jedem der möglichen Anwendungsfälle zu beschreiben.

Anmerkung: Sofern nicht anders angegeben, stammen alle Zitate aus dem OASIS Standard


Was sind Eigenschaften in MQTT v5.0

Eigenschaften sind Teil der „Erweiterungsmechanismen“, die in MQTT v5.0 hinzugefügt wurden. In der Vorgängerversion v3.1.1, der letzten Version vor diesem großen Upgrade, gab es sie noch nicht. Sie sind in MQTT v5.0 allgegenwärtig. Aber was sind MQTT-Eigenschaften? Eigenschaften wovon genau?

Eigenschaften der Anwendungsnachricht ist die Antwort. In der Terminologie des OASIS-Standards ist die Anwendungsmeldung:

„Die Daten, die das MQTT-Protokoll über das Netzwerk für die Anwendung überträgt. Wenn eine Anwendungsnachricht durch MQTT transportiert wird, enthält sie Nutzdaten (Payload data), eine Quality of Service (QoS), eine Sammlung von Eigenschaften und einen Topic-Namen.“ (Hervorhebung durch uns)

Bitte sehen Sie sich das gelbe Rechteck an, das die „Nutzdaten“ in Abbildung 1 unten darstellt. Es gibt einen wichtigen terminologischen Unterschied, auf den wir Sie hier aufmerksam machen möchten.

Abstraktes Diagramm der MQTT 5.0-Anwendungsnachricht

Abb.01 – Abstraktes Diagramm einer MQTT 5.0-Anwendungsnachricht

Wenn wir im Zusammenhang mit einem Protokoll zur gemeinsamen Nutzung von Nachrichten das Wort „Nachricht“ sehen, denken wir gewöhnlich an die Nutzernachricht, häufig eine Textnachricht. Meistens denken wir nicht an die Botschaft, sondern an die Anwendung als Ganzes. 

Aber hier ist die von den Nutzern über MQTT gesendete Nachricht Teil der Nutzdaten und die Eigenschaften sind Teil des abstrakten Protokollmodells namens Anwendungsnachricht. Wenn wir also eine Nutzernachricht über MQTT senden, können wir nicht nur Eigenschaften haben, die sich auf diese „Nutzernachricht“ beziehen, sondern auch Eigenschaften, die sich auf die Anwendungsnachricht als Ganzes beziehen: Eigenschaften für die Verbindung, Eigenschaften für die Veröffentlichung, Eigenschaften für das Abonnieren und Abbestellen von Themen, Eigenschaften für die Authentifizierung und so weiter.

Außerdem gibt es die Will Properties, die mit der Will Message verbunden sind.

"The Will Message consists of the Will Properties, Will Topic, and Will Payload fields in the CONNECT Payload." (Die Will-Meldung besteht aus den Feldern Will-Eigenschaften, Will-Thema und Will-Nutzlast in der CONNECT-Nutzlast.)

Diese Terminologie kann etwas verwirrend sein, wenn man mit der Implementierung des Protokolls beginnt, aber wir werden unser Bestes tun, um es so klar wie möglich zu machen.


Wozu werden Eigenschaften verwendet?

Neben der Übertragung von Nutzdaten-Metadaten können Eigenschaften zur Konfiguration aller Aspekte der Interaktion zwischen dem Client und dem Server (Broker) und auch der Interaktion zwischen verschiedenen Clients verwendet werden. Von der Verbindung bis zur Trennung können sie u. a. dazu verwendet werden, das Format des Inhaltstyps festzulegen, Informationen vom Broker anzufordern, eine Ablaufzeit für die Nachricht zu definieren, eine Authentifizierungsmethode auszuwählen und sogar eine Serverumleitung durchzuführen. Wie in den Tabellen unten zu sehen ist, können alle Pakettypen mit Ausnahme der PINGREQ- und PINGRESP-Pakete, die zur Auffrischung der Keep-Alive-Periode verwendet werden, je nach Paketkontext einige spezifische Eigenschaften aufweisen.

Die Nutzereigenschaft ist ein Sonderfall einer Eigenschaft, die in allen Paketen verwendet werden kann und deren Bedeutung von der Anwendung definiert wird, d. h. ihre Semantik ist nicht durch das Protokoll festgelegt. Wir werden im letzten Abschnitt eine kurze Einführung in die Nutzereigenschaft geben, in der wir darüber sprechen, wie Eigenschaften zur Erweiterung des Protokolls verwendet werden können.

Auch wenn die Namen der Eigenschaften ihren Zweck deutlich machen, müssen wir das wissen:

  • wann sie verwendet werden können
  • wann sie verwendet werden müssen
  • und was passiert, wenn sie falsch eingestellt sind

Um das Lesen und Verstehen der folgenden Beschreibungen zu erleichtern, haben wir sie in der nachstehenden Tabelle entsprechend ihrer Funktion in verschiedenen Farben gruppiert. Bitte beachten Sie, dass die Gruppierung etwas willkürlich ist, da sich die Verwendung von Eigenschaften zwischen verschiedenen Pakettypen überschneidet.

In den folgenden Beschreibungen werden die Begriffe MUSS (MUST) und KANN (MAY) so verwendet, wie sie im OASIS-Standard verwendet werden, der sie wiederum wie in IETF RFC 2119 beschrieben verwendet.


Eigenschaften der Verbindung
CONNECT Session Expiry Interval, Receive Maximum, Maximum Packet Size, Topic Alias Maximum, Request Response Information, Request Problem Information, User Property, Authentication Method, Authentication Data
CONNECT Nutzlast Will Delay Interval, Payload Format Indicator, Message Expiry Interval, Content Type, Response Topic, Correlation Data, User Property
CONNACK Session Expiry Interval, Receive Maximum, Maximum QoS, Retain Available, Maximum Packet Size, Assigned Client Identifier, Topic Alias Maximum, Reason String, User Property, Wildcard Subscription Available, Subscriptions Identifiers Available, Shared Subscription Available, Server Keep Alive, Response Information, Server Reference, Authentication Method, Authentication Data
DISCONNECT Session Expiry Interval, Reason String, User Property, Server Reference

Tabelle 1: MQTT v5.0 – Eigenschaften gruppiert nach Funktionalität – Verbindungseigenschaften

Assigned Client Identifier – MUSS bei CONNECT gesetzt werden. Ist dies nicht der Fall, KANN der Broker bei CONNACK eine Kennung vergeben.

Der Client Identifier ist für CONNECT-Pakete obligatorisch. Der Broker darf jedoch eine Kennung mit einer Länge von null Bytes akzeptieren und dem Clienten eine Kennung zuweisen. In diesem Fall wird der Broker sie in einem CONNACK-Paket unter dieser Eigenschaft zurücksenden.

Maximale Paketgröße – Kann nicht festgelegt werden, darf aber nicht auf Null gesetzt werden.

Die maximale Paketgröße „ist die Gesamtzahl der Bytes in einem MQTT-Kontrollpaket“. Sie wird vom Client und vom Broker verwendet, um die maximale Paketgröße festzulegen, die sie zu akzeptieren bereit sind. Wenn unser Client diese Eigenschaft einstellt und der Broker ein Paket sendet, das diesen Grenzwert überschreitet, müssen wir mit Reason Code 0x95 (Paket zu groß) DISCONNECT durchführen. Der Broker sendet keine Reason String(s) oder einige User Properties, wenn deren Einbeziehung das Paket größer als diese Eigenschaft machen würde.

Maximale QoS – wird vom Broker bei CONNACKs verwendet und KANN nicht gesetzt werden.

Die maximale QoS informiert den Clienten über die Fähigkeit des Brokers, QoS-Stufen zu handhaben. Wenn der Broker die QoS-Stufe 2 akzeptiert, wird diese Eigenschaft nicht gesetzt. Unser Client MUSS sich an diese Serverbeschränkung halten, indem er keine PUBLISH-Pakete mit einem höheren QoS-Level sendet. Wir können in unserem Client nur QoS Level 0 unterstützen und trotzdem konform sein.

Nicht-normativer Kommentar

„Ein Client muss keine QoS 1 oder QoS 2 PUBLISH-Pakete unterstützen. Wenn dies der Fall ist, beschränkt der Client einfach das maximale QoS-Feld in allen SUBSCRIBE-Befehlen, die er sendet, auf einen Wert, den er unterstützen kann.“

Message Expiry Interval – Darf nicht festgelegt werden.

Das Ablaufintervall der Nachricht ist ebenfalls Teil der PUBLISH-Eigenschaften. Sie legt die Lebensdauer der Will-Nachricht fest. Wenn diese Option nicht gesetzt ist, hat der Broker bei der Veröffentlichung der Will-Nachricht keine definierte Verfallszeit.

„Wenn das Verfallsintervall der Nachricht abgelaufen ist und der Server die Weiterleitung an einen passenden Abonnenten nicht starten konnte, MUSS er die Kopie der Nachricht für diesen Abonnenten löschen.“

Sie kann auch in den Will-Eigenschaften für die CONNECT-Nutzlast eingestellt werden. Wenn sie nicht gesetzt ist, läuft die Nachricht nicht ab.

Payload Format Indicator – MUSS gesetzt werden, wenn wir Zeichendaten senden.

Der Payload Format Indicator kann in den Will Properties für die CONNECT Payload gesetzt werden. Hier wird angegeben, ob es sich bei der Will-Nachricht um UTF-8-kodierte Zeichendaten oder um „nicht spezifizierte Bytes“ handelt. 

„Der Server MUSS überprüfen, ob die Will Message das angegebene Format hat, und wenn nicht, eine CONNACK mit dem Reason Code 0x99 (Payload format invalid) senden.

Sie ist Teil der PUBLISH-Eigenschaften und gibt das Format der Payload an. Die Validierung des Brokers ist ebenfalls optional. Wenn es jedoch das Format der Nutzdaten validiert, können wir ein PUBACK, PUBREC oder DISCONNECT mit demselben Reason Code (0x99) erwarten, wenn das Format von dem angekündigten abweicht.

Ist diese Option nicht gesetzt, wird angenommen, dass das Payload-Format „unspecified bytes“ ist. Das heißt, wenn wir Zeichendaten senden, ist die Einstellung dieser Eigenschaft obligatorisch.

Reason String – er KANN bei allen ACKs, DISCONNECT und AUTH vom Client oder Broker verwendet werden.

Der Reason String ist eine der neuen Funktionen in MQTT v5.0. Er kann verwendet werden, um den Reason Code durch ein von Menschen lesbares Diagnoseinstrument zu ergänzen. Falls vorhanden, kann er z. B. für die Protokollierung verwendet werden. Wir können den Broker bitten, sie nicht zu senden, indem wir in den CONNECT-Eigenschaften die Option „Request Problem Information“ (Probleminformationen anfordern) auf Null setzen. 

Receive Maximum – Kann nicht festgelegt werden, darf aber nicht auf Null gesetzt werden. 

Unser Client kann diese Eigenschaft von CONNECT verwenden, um die Anzahl der QoS-1- und QoS-2-Publikationen zu begrenzen, die wir auf der aktuellen Netzwerkverbindung gleichzeitig verarbeiten wollen. Der Broker kann sie auf CONNACK setzen. Wenn sie nicht eingestellt ist, wird sie standardmäßig auf 65.535 gesetzt.

Bei QoS 0 besteht keine Notwendigkeit, auf PUBACK (QoS 1) oder PUBCOMP (QoS 2) zu warten, da QoS 0 bekanntlich „fire and forget“ ist. Diese Eigenschaft legt fest, wie viele Nachrichten unser Client oder der Broker senden/empfangen will, vor dem Erhalt der entsprechenden PUBACK oder PUBCOMP. Man kann sich das als ein Mittel vorstellen, um festzulegen, wie viele Nachrichten im Zustand „ausstehende Bestätigung“ sein können, bis neue Nachrichten gesendet werden können.

Request Problem Information – KANN in CONNECT eingestellt werden.

Die Request Problem Information wird verwendet, um dem Server mitzuteilen, dass wir im Falle von Fehlern Reason String(s) und User Properties (Gründe und Eigenschaften) erhalten wollen. Wenn wir nichts sagen – indem wir sie nicht einstellen – kann der Broker sie schicken.

Request Response Information – KANN bei CONNECT eingestellt werden.

Die Request-Response-Information ist Teil der Request/Response-Interaktion über MQTT, anstelle der regulären Pub/Sub-Interaktion. Wenn diese Option nicht gesetzt ist, teilen wir dem Broker mit, dass wir nicht möchten, dass er Response Information (Antwortinformation) sendet. Andernfalls erlauben wir dem Broker, uns diese Antwort zu senden. Es ist zu beachten, dass der Broker keine Antwortinformationen senden darf, auch wenn wir sie anfordern. Wenn diese Eigenschaft nicht vorhanden ist, ist der Wert standardmäßig nicht gesetzt.

Response Information – KANN bei CONNECT eingestellt werden.

Die Antwortinformationen sind Teil der Anfrage/Antwort-Interaktion über MQTT, anstelle der regulären Pub/Sub-Interaktion. 

Nicht-normativer Kommentar

„Eine übliche Anwendung ist die Übergabe eines global eindeutigen Teils des Themenbaums, der für diesen Client mindestens für die Dauer seiner Sitzung reserviert ist. Dies kann oft nicht einfach ein zufälliger Name sein, da sowohl der anfragende als auch der antwortende Client berechtigt sein müssen, ihn zu verwenden. Es ist üblich, dies als Wurzel eines Themenbaums für einen bestimmten Client zu verwenden. Damit der Server diese Informationen zurückgeben kann, muss er normalerweise korrekt konfiguriert sein. Mit diesem Mechanismus kann diese Konfiguration einmal im Server und nicht in jedem Client vorgenommen werden.“

Response Topic – KANN auf CONNECT oder PUBLISH gesetzt werden. 

Response Topic (Antwort-Thema) ist Teil der Anfrage/Antwort-Interaktion über MQTT, anstelle der regulären Pub/Sub-Interaktion. Wenn wir sie einbeziehen, interpretiert der Broker die Will-Nachricht als eine Anfrage. Anders als der Topic-Filter, der für SUBSCRIBE-Pakete verwendet wird, darf das Antwort-Topic keine Platzhalterzeichen enthalten.

„Eine Anforderungsnachricht ist eine Anwendungsnachricht mit einem Antwortthema“.

Dies ist also die Eigenschaft, die eine Anwendungsnachricht als Teil einer Anfrage/Antwort-Interaktion kennzeichnet.

Retain Available – KANN auf CONNACK vorhanden sein.

Die Eigenschaft Retain Available (verfügbar halten) informiert unseren Clienten darüber, ob der Broker Retain-Nachrichten unterstützt. Bei Abwesenheit sind aufbewahrte Nachrichten verfügbar.

Server Keep Alive – KANN bei CONNACK vorhanden sein.

Die Keep-Alive-Eigenschaft (am Leben halten) des Servers hat Vorrang vor der Keep-Alive-Anforderung des Clients bei CONNECT. Wenn diese Eigenschaft bei CONNACK nicht vorhanden ist, können wir unser Keep Alive verwenden. Andernfalls gelten die Server Keep Alive-Regeln.

Server Reference – KANN bei CONNACK oder DISCONNECT vorhanden sein.

Die Server Reference informiert unseren Client über die Server-Umleitung. Es kann sich um eine vorübergehende oder dauerhafte Umleitung handeln. In beiden Fällen kann der andere Server unserem Client bereits bekannt sein oder er wird über diese Eigenschaft angegeben.

Nicht-normativer Kommentar

Beispiele für die Server-Referenz sind:

myserver.xyz.org

myserver.xyz.org:8883

10.10.151.22:8883 [fe80::9610:3eff:fe1c]:1883

Der Broker darf diese Eigenschaft nie übermitteln und unser Client darf sie ignorieren.

Session Expiry Interval – KANN bei CONNECT eingestellt werden.

Das Sitzungsintervall legt fest, wie lange die Sitzung nach einem Verbindungsabbruch aufrechterhalten werden soll. Wenn diese Option nicht gesetzt ist oder fehlt, endet die Sitzung, wenn die Verbindung geschlossen wird. Es ist möglich, die Sitzung so einzustellen, dass sie nicht abläuft, indem diese Eigenschaft auf UINT_MAX gesetzt wird. Wir müssen den Sitzungsstatus speichern, wenn diese Eigenschaft größer als Null ist. Wir können dies anhand des Flags Session Present auf CONNACK überprüfen.

Diese Eigenschaft kann nützlich sein, wenn die Netzwerkverbindung unterbrochen ist, damit unser Client die Sitzung wieder aufnehmen kann, sobald die Netzwerkverbindung wiederhergestellt ist.

Shared Subscription Available – KANN auf CONNACK vorhanden sein.

Die Option Shared Subscription Available (Geteiltes Abonnement verfügbar) informiert unseren Clienten darüber, ob der Broker Shared Subscriptions unterstützt. Ist dies nicht der Fall, so unterstützt der Broker diese Funktion.

Subscription Identifiers Available – KANN auf CONNACK vorhanden sein.

Die Angabe „Subscription Identifiers Available“ (Abonnement-Identifikatoren verfügbar) informiert unseren Clienten darüber, ob der Broker Subscription Identifiers unterstützt. Ist dies nicht der Fall, so unterstützt der Broker diese Funktion.

Topic Alias Maximum – KANN bei CONNECT gesetzt werden und KANN bei CONNACK vorhanden sein.

Das Topic Alias Maximum informiert den Broker über die maximale Anzahl von Topic Alias, die unser Client für diese spezielle Verbindung akzeptieren möchte. Wenn wir den Wert auf Null setzen oder ihn leer lassen, sendet der Broker keinen Topic Alias in dieser Verbindung. Der umgekehrte Fall ist ebenfalls zutreffend: Wenn diese Eigenschaft bei CONNACK nicht vorhanden ist oder vorhanden ist, aber der Wert Null ist, darf unser Client keinen Topic Alias senden.

Wildcard Subscription Available – KANN auf CONNACK vorhanden sein.

Wenn diese Eigenschaft nicht gesetzt ist (auf Null gesetzt), unterstützt der Broker keine Wildcard-Abonnements. In diesem Fall wird der Broker DISCONNECT, nachdem er ein SUBSCRIBE erhalten hat, das ein Wildcard-Abonnement anfordert. Aber selbst wenn der Broker die Funktion unterstützt, darf er eine bestimmte Abonnementanfrage, die Wildcard-Abonnements enthält, zurückweisen und ein SUBACK mit demselben Reason Code 0xA2 (Wildcard Subscriptions not supported) zurückgeben. Wenn die Eigenschaft in CONNACK nicht vorhanden ist, unterstützt der Broker die Funktion.

Will Delay Interval – KANN in den Will-Eigenschaften der CONNECT-Nutzlast eingestellt werden.

Diese Eigenschaft legt die Verzögerung in Sekunden fest, die der Broker einhalten muss, bevor er die Will-Nachricht sendet. Diese Eigenschaft ist besonders nützlich, um zu vermeiden, dass die Will-Nachricht bei instabilen oder unterbrochenen Netzverbindungen gesendet wird.


Eigenschaften zur Veröffentlichung
PUBLISH Payload Format Indicator, Message Expiry Interval, Topic Alias, Response Topic, Correlation Data, User Property, Subscription Identifier, Content Type
PUBACK Reason String, User Property
PUBREC Reason String, User Property
PUBREL Reason String, User Property
PUBCOMP Reason String, User Property

Tabelle 2: MQTT v5.0 – Eigenschaften gruppiert nach Funktionalität – Veröffentlichungseigenschaften

Topic Alias – KANN bei PUBLISH gesetzt werden

Topic Alias ist ebenfalls eine neue Funktion von MQTT v5.0. Es ermöglicht dem Broker oder dem Client, die Größe der Pakete zu reduzieren, indem der Topic Name durch eine kleine Ganzzahl, dem Alias, ersetzt wird. Die Verringerung des Overheads kann ausdrucksstark sein, da Topic-Namen Zeichenketten sind, die bis zu 65.535 Bytes (UINT_MAX) umfassen können.

Correlation Data – KANN auf PUBLISH und Will Properties eingestellt werden

Die Korrelationsdaten sind Teil der Anfrage/Antwort-Interaktion über MQTT, anstelle der regulären Pub/Sub-Interaktion. Sein Wert hat nur für die Anwendung (Broker und Clients) Bedeutung. Es handelt sich um binäre Daten, die in Request/Response „vom Absender der Request Message verwendet werden, um zu identifizieren, für welche Anfrage die Response Message bestimmt ist, wenn sie empfangen wird“.

Content Type – KANN bei PUBLISH und Will Properties eingestellt werden

Content Type kann auch in CONNECT zum Einstellen des Content Types von Will Message verwendet werden. 

Der Broker validiert nur die Kodierung der Eigenschaft selbst. Es ist Sache des Clienten, die Bedeutung dieser Eigenschaft zu bestimmen.


Eigenschaften zum Abonnieren/Abbestellen
SUBSCRIBE Subscription Identifier, User Property
SUBACK Reason String, User Property
UNSUBSCRIBE User Property
UNSUBACK  Reason String, User Property

Tabelle 3: MQTT v5.0 – Eigenschaften gruppiert nach Funktionalität – Eigenschaften zum Abonnieren/Abbestellen

Subscription Identifier – KANN bei SUBSCRIBE festgelegt werden

Dies ist eine numerische Kennung, die bei SUBSCRIBE eingestellt werden kann. Sie wird auf der Nachricht vom Broker zurückgegeben, sodass der/die Client(en) feststellen kann/können, welche(s) Abonnement(s) die Zustellung der Nachricht verursacht hat/haben. Sie kann den Wert von 1 bis 268.435.455 haben. DARF nicht auf Null gesetzt werden und DARF nicht in PUBLISH vom Client zum Server verwendet werden.


Eigenschaften der Authentifizierung
AUTH
Authentication Method, Authentication Data, Reason String, User Property

Tabelle 4: MQTT v5.0 – Eigenschaften gruppiert nach Funktionalität – Authentifizierungseigenschaften

Selbstverständlich können diese Eigenschaften auch auf Verbindungen angewendet werden.

Authentication Method

Neben der grundlegenden Netzwerkauthentifizierung mit Nutzername und Passwort erlaubt MQTT v5.0 die ‚Erweiterte Authentifizierung“. Diese Eigenschaft gibt Aufschluss über die zu wählende Methode. Die Methode der Wahl wird von den Anwendungsentwicklern festgelegt. Der Broker informiert, ob die Methode unterstützt wird.

Die Authentifizierungsmethode ist in der Regel ein SASL-Mechanismus, und die Verwendung eines solchen registrierten Namens erleichtert den Austausch. Die Authentifizierungsmethode ist jedoch nicht auf die Verwendung registrierter SASL-Mechanismen beschränkt

Authentication Data

Diese Eigenschaft wird vom Client und vom Broker für den Austausch von Authentifizierungsdaten je nach der gewählten Authentifizierungsmethode verwendet.


Wie wir MQTT v5.0 Properties in MQL5 lesen

Bisher haben wir uns in den vorangegangenen Teilen dieser Serie mit Einstellungen „pro Sitzung“ befasst, die über Bit-Flags konfiguriert werden, nämlich die Connect-Flags bei CONNECT, den CONNACK Reason Code und das CONNACK Session Present Flag. Diese Einstellungen werden einmal pro Sitzung gelesen/geschrieben/aufrechterhalten. Aber bei den Eigenschaften sind die Dinge anders. Sie sind Teil der Application Message und können in einigen Anwendungsprofilen große Mengen an kritischen Daten enthalten. Unser Client muss also darauf vorbereitet sein, ständig Eigenschaften zu lesen und zu schreiben.

Um einen Test für das Lesen der vom Server gesendeten Eigenschaften zu schreiben, benötigen wir ein Byte-Array als Beispiel. Wir beginnen mit einem Beispiel-Byte-Array für ein CONNACK-Paket, da dies das erste Paket ist, mit dem unser Client zu tun haben wird. Wie alle MQTT-Kontrollpakete hat es einen zwei Byte großen festen Header und einen zwei Byte großen variablen Header, der ein Byte für die Connect Acknowledge Flags und ein Byte für den Connect Reason Code enthält. Die Eigenschaften sind das letzte Feld im CONNACK-Paket, das keine Paketkennung und keine Nutzlast enthält.

MQTT 5.0 – Aufbau eines CONNACK-Pakets

Abb. 02: Aufbau eines MQTT 5.0 CONNACK-Pakets

Aus dem Standard wissen wir das:

„Der Satz von Eigenschaften besteht aus einer Eigenschaftslänge, gefolgt von den Eigenschaften.“

Das wissen wir auch:

„Die Eigenschaftslänge ist als variables Byte Integer kodiert. Die Eigenschaftslänge umfasst nicht die Bytes, die zur Kodierung selbst verwendet werden, sondern die Länge der Eigenschaften. Wenn es keine Eigenschaften gibt, MUSS dies durch die Angabe einer Eigenschaftslänge von Null angegeben werden.“

Daher sind die verbleibende Länge des Fixed Header und die Property Length, die beide als Variable Byte Integer kodiert sind, die ersten Informationen, die wir vor dem Zugriff auf die Eigenschaften lesen müssen. Wenn die Eigenschaftslänge Null ist, gibt es nichts zu lesen.

Unser Byte-Array-Beispiel könnte also für einen CONNACK ohne Eigenschaften wie folgt aussehen:

uchar connack_response[] = {2, X, 0, 0, 0};

Dabei ist X die verbleibende Länge des festen Kopfes. Der Algorithmus zur Dekodierung eines variablen Byte Integer wird vom Standard bereitgestellt. In MQL5 kann dieser Algorithmus wie folgt geschrieben werden:

uint DecodeVariableByteInteger(uint &buf[], uint idx)
  {
   uint multiplier = 1;
   uint value = 0;
   uint encodedByte;
   do
     {
      encodedByte = buf[idx];
      value += (encodedByte & 127) * multiplier;
      if(multiplier > 128 * 128 * 128)
        {
         Print("Error(Malformed Variable Byte Integer)");
         return -1;
        }
      multiplier *= 128;
     }
   while((encodedByte & 128) != 0);
   return value;
  };

Dabei steht buf[idx] für das „nächste Byte aus dem Stream“.

Obwohl der Algorithmus für die Dekodierung eines variablen Byte Integer vom Standard zur Verfügung gestellt wird, haben wir auch dafür einen sehr einfachen Test geschrieben, nur um sicherzustellen, dass die Implementierung in diesem Stadium wie erwartet funktioniert:

bool TEST_DecodeVariableByteInteger()
  {
   Print(__FUNCTION__);
   uint buf[] = {1, 127, 0, 0, 0};
   uint expected = 127;
   uint result = DecodeVariableByteInteger(buf, 1);
   ZeroMemory(buf);
   return AssertEqual(expected, result);
  }

Zu Testzwecken wird der verbleibende Längenwert natürlich hart kodiert. Für das obige CONNACK ohne Eigenschaften wäre dies der Fall:

uchar connack_response[] = {2, 3, 0, 0, 0};

Ein Beispiel für ein Byte-Array für ein CONNACK mit der Eigenschaft „Payload Format Indicator“, die auf „UTF-8 Encoded String payload format“ eingestellt ist, könnte wie folgt aussehen:

uchar connack_response_one_byte_property = {2, 5, 0, 0, 2, 1, 1};

Wie man sehen kann, ist die Überprüfung auf das Vorhandensein von Eigenschaften in einem CONNACK ziemlich einfach. Wir müssen nur das fünfte Byte lesen, das die Eigenschaftslänge enthält. Wenn es nicht Null ist, haben wir Eigenschaften:

bool TestProtectedMethods::TEST_HasProperties_CONNACK_No_Props()
  {
   Print(__FUNCTION__);
//--- Arrange
   bool expected = false;
   uchar connack_no_props[5] = {2, 3, 0, 0, 0};
//--- Act
   CSrvResponse *cut = new CSrvResponse();
   bool result =  this.HasProperties(connack_no_props);
//--- Assert
   bool isTrue = AssertEqual(expected, result);
//--- cleanup
   delete cut;
   ZeroMemory(result);
   return  isTrue ? true : false;
  }

Bitte werfen Sie einen Blick auf unseren vorherigen Artikel, um zu sehen, wie wir geschützte Methoden testen, und werfen Sie auch einen Blick auf den beigefügten Code, um die FAIL-Variante des Tests zu sehen.

Die erste Implementierung, die gerade ausreicht, um die Tests in der jetzigen Phase zu bestehen, sieht wie folgt aus:

bool CSrvResponse::HasProperties(uchar &resp_buf[])
  {
   return resp_buf[4] != 0 ? true : false;
  }

Hier verwenden wir den ternären Operator, um den Code auf ein Minimum zu beschränken.

Es ist zu beachten, dass die Position des Bytes Property Length vom Pakettyp abhängt. Das liegt daran, dass die Eigenschaften zwar immer das letzte Feld im variablen Header sind, es aber Pakete gibt, die vor der Eigenschaftslänge einen zwei Byte langen Packet Identifier benötigen. In einem CONNACK ist dies kein Thema.

„Dann funktioniert dieser Code nicht für andere Pakettypen“, werden Sie vielleicht sagen. Und ja, Sie haben Recht. Aber denken Sie bitte daran, dass wir hier einen TDD-Ansatz verwenden. Einer der Hauptvorteile dieser Praxis besteht darin, dass wir uns auf die anstehende Aufgabe konzentrieren und NICHT versuchen, mit all den möglichen zukünftigen Problemen fertig zu werden, während wir uns in den ersten Phasen der Entwicklung befinden. Wir werden uns mit anderen Pakettypen beschäftigen, wenn die Zeit reif ist und unser Test fehlschlägt. Dann werden wir unsere Tests neu schreiben und den Code eventuell überarbeiten.

Auch wenn es ein wenig kontraintuitiv erscheinen mag, kann man keinen Code mehr schreiben, wenn man sich einmal daran gewöhnt hat. Wenn nicht aus anderen Gründen, dann weil es unsere Arbeit erleichtert und sogar Freude bereitet. Übrigens, im nächsten Teil dieser Serie werden wir mit dem Schreiben und Lesen von Eigenschaften für PUBLISH-Pakete beginnen. Bleiben Sie also dran!

Wenn die Eigenschaftslänge ungleich Null ist, können wir im nächsten Byte nach einem Eigenschaftsbezeichner suchen. Der Property Identifier gibt uns den Datentyp der Eigenschaft an.

„Eine Eigenschaft besteht aus einem Identifikator, der ihre Verwendung und ihren Datentyp definiert, gefolgt von einem Wert.“

uchar CSrvResponse::GetPropertyIdentifier(uchar &resp_buf[])
  {
   return resp_buf[5];
  }

Der Datentyp gibt uns die Anzahl der zu lesenden Bytes an. Der Datentyp kann einer der folgenden sein:

Ein Byte Ganzzahl

uchar CSrvResponse::ReadOneByteProperty(uchar &resp_buf[])
  {
   return resp_buf[6];
  }

Zwei-Byte Ganzzahl

void CSrvResponse::ReadTwoByteProperty(uchar &resp_buf[], uchar &dest_buf[])
  {
   ArrayCopy(dest_buf, resp_buf, 0, 6, 2);
  }

Vier Byte Ganzzahl

void CSrvResponse::ReadFourByteProperty(uchar &resp_buf[], uchar &dest_buf[])
  {
   ArrayCopy(dest_buf, resp_buf, 0, 6, 4);
  }

Variables Byte Integer (nur für Abonnement-Identifikator) 

void CSrvResponse::ReadVariableByteProperty(uint &resp_buf[], uint &dest_buf[], uint start_idx)
  {
   uint value = DecodeVariableByteInteger(resp_buf, start_idx);
   ArrayResize(dest_buf,value,7);
   ArrayFill(dest_buf, 0, 1, value);
  }

Das Lesen/Dekodieren (und Schreiben/Kodieren) dieser Eigenschaft erfordert viel mehr als das, was dieser Test im Moment überprüft.

„Das Variable Byte Integer wird mit einem Kodierungsschema kodiert, das ein einzelnes Byte für Werte bis 127 verwendet. Größere Werte werden wie folgt behandelt. Die niedrigstwertigen sieben Bits eines jeden Bytes kodieren die Daten, und das höchstwertige Bit wird verwendet, um anzuzeigen, ob in der Darstellung noch weitere Bytes folgen. Somit kodiert jedes Byte 128 Werte und ein 'Fortsetzungsbit' (Hervorhebung durch uns)

Bei der Implementierung von SUBSCRIBE-Paketen werden wir uns mit Variable Byte Integer-Eigenschaften befassen, da diese nur für die Eigenschaft Subscription Identifier verwendet werden. Auch die folgenden drei Datentypen: UTF-8-kodierte Zeichenfolgen, binäre Daten und UTF-8-Zeichenfolgenpaare. Sie werden im Zusammenhang mit der Verwendung von Request/Response und der Implementierung des Sonderfalls der User Property näher erläutert.

UTF-8-kodierten Zeichenketten wird ihre Länge vorangestellt.

„Jeder dieser Zeichenketten ist ein Zwei-Byte-Integer-Längenfeld vorangestellt, das die Anzahl der Bytes in der UTF-8-kodierten Zeichenkette selbst angibt, wie in Abbildung 1.1 Struktur von UTF-8-kodierten Zeichenketten unten dargestellt. Folglich beträgt die maximale Größe einer UTF-8-kodierten Zeichenfolge 65.535 Byte. Wenn nicht anders angegeben, können alle UTF-8-kodierten Zeichenketten eine beliebige Länge im Bereich von 0 bis 65.535 Byte haben.“

MQTT-v5-utf8-kodierte-Zeichenketten-Struktur-OASIS

Abb. 03: MQTT 5.0 – Struktur von UTF-8 kodierten Strings – Bildschirmausschnitt aus der OASIS-Tabelle

Es sei darauf hingewiesen, dass UTF-8-kodierte Zeichenketten auf das Vorhandensein unzulässiger Unicode-Codepunkte überprüft werden müssen (mehr dazu später).

„Abschnitt 1.6.4 beschreibt die unzulässigen Unicode-Codepunkte, die nicht in einer UTF-8-kodierten Zeichenfolge enthalten sein dürfen. Eine Client- oder Server-Implementierung kann wählen, ob sie überprüfen will, dass diese Codepunkte nicht in UTF-8-kodierten Strings wie dem Topic-Namen oder den Eigenschaften verwendet werden.“

Den binären Daten wird auch ihre Länge vorangestellt.

„Binäre Daten werden durch eine Zwei-Byte-Integer-Länge dargestellt, die die Anzahl der Datenbytes angibt, gefolgt von dieser Anzahl von Bytes. Daher ist die Länge der Binärdaten auf den Bereich von 0 bis 65.535 Bytes begrenzt.“

Wir zählen die Anzahl der gelesenen Bytes, damit wir wissen, wann wir alle Eigenschaften gelesen haben. Wir müssen uns nicht um die Reihenfolge der Eigenschaften kümmern.

„Es gibt keine Bedeutung in der Reihenfolge der Eigenschaften mit unterschiedlichen Identifikatoren.


Wie Eigenschaften zur Erweiterung des Protokolls verwendet werden können

Wie am Anfang dieses Artikels erwähnt, sind Eigenschaften Teil des „Erweiterungsmechanismus“ von MQTT 5.0 und die bekannteste Eigenschaft für diesen Mechanismus ist die User Property, die in jedem MQTT Control Packet verwendet werden kann. Nutzereigenschaften sind Schlüssel-Wert-Paare, deren Bedeutung für das Protokoll undurchsichtig ist. Das heißt, seine Bedeutung wird durch die Anwendung definiert.

Stellen wir uns einen Anwendungsfall für unseren Bereich vor: Ein Empfänger kopiert Handelssignale von drei verschiedenen Anbietern. Jeder Anbieter arbeitet mit einem anderen Broker zusammen. Jeder Broker kann demselben Vermögenswert, z. B. Gold, unterschiedliche Symbolnamen zuweisen.

  • Broker A verwendet GOLD
  • Broker B verwendet XAUUSD
  • Broker C verwendet XAUUSD.s

Außerdem kann jeder Signalanbieter mehr als einen Broker verwenden. Das Paar signal_provider : provider_broker kann sich also jederzeit ändern, auch während einer laufenden Handelssitzung. (Ja, wir haben es hier mit einer quasi-kombinatorischen Explosion zu tun). Der Empfänger muss die Bedeutung des Symbolnamens, den er empfängt, idealerweise in Millisekunden kennen, um ihn in den Symbolnamen übersetzen zu können, den sein Broker verwendet, um die Handelsanfrage korrekt zu replizieren.

Ohne Nutzereigenschaften, wie es bei früheren Versionen des Protokolls der Fall war, müssten diese Metadaten (signal_provider : provider_broker) in die Nutzdaten eingebettet werden, wo man erwarten würde, nur die erforderlichen Handelssignaldaten zu finden (und zu parsen).

Wenn dagegen jeder Signalanbieter seine eigene Nutzereigenschaft mit seinem Brokernamen hat, kann die Nutzlast nur die erforderlichen Signaldaten enthalten.

Dies ist ein vereinfachtes Beispiel für diesen Anwendungsfall. Aber denken Sie daran, dass diese Metadaten auf alle wichtigen Informationen, einschließlich JSON/XML-Strings und sogar ganze Dateien, ausgedehnt werden können. Die Möglichkeiten sind also in gewisser Weise unbegrenzt.


Schlussfolgerung

In diesem vierten Teil unserer Serie haben wir eine kurze Beschreibung der Eigenschaften in MQTT v5.0, ihrer Semantik und einiger Anwendungsfälle gegeben. Wir haben auch berichtet, wie wir sie für CONNACK implementieren, und ein einfaches Beispiel dafür geliefert, wie sie zur Erweiterung des Protokolls verwendet werden können. Im nächsten Teil werden wir sie im Zusammenhang mit den PUBLISH-Paketen anwenden, wobei wir stets einen TDD-Ansatz verwenden, um die Komplexität der Spezifikationen zu bewältigen.

Wenn Sie der Meinung sind, dass Sie zur Entwicklung dieses nativen MQL5-Clients beitragen können, der Teil unserer Code Base sein wird, schreiben Sie uns bitte eine Nachricht in den Kommentaren unten oder in unserem Chat. Jede Hilfe ist willkommen! :)


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/13651

Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 3): Hinzufügen von Symbolpräfixen und/oder -suffixen und der Handelszeiten Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 3): Hinzufügen von Symbolpräfixen und/oder -suffixen und der Handelszeiten
Mehrere Handelskollegen schickten E-Mails oder äußerten sich dazu, wie man diesen Multi-Currency EA bei Brokern mit Symbolnamen mit Präfixen und/oder Suffixen verwenden kann, und auch dazu, wie man Handelszeitzonen oder Handelszeitsitzungen bei diesem Multi-Currency EA implementiert.
Datenwissenschaft und maschinelles Lernen (Teil 15): SVM, ein Muss im Werkzeugkasten jedes Händlers Datenwissenschaft und maschinelles Lernen (Teil 15): SVM, ein Muss im Werkzeugkasten jedes Händlers
Entdecken Sie die unverzichtbare Rolle von Support Vector Machines (SVM) bei der Gestaltung der Zukunft des Handels. Dieser umfassende Leitfaden zeigt auf, wie SVM Ihre Handelsstrategien verbessern, die Entscheidungsfindung optimieren und neue Chancen auf den Finanzmärkten erschließen kann. Tauchen Sie ein in die Welt der SVM mit realen Anwendungen, Schritt-für-Schritt-Tutorials und Expertenwissen. Rüsten Sie sich mit dem unverzichtbaren Werkzeug aus, das Ihnen helfen kann, die Komplexität des modernen Handels zu bewältigen. Verbessern Sie das Spiel Ihres Handels mit SVM - ein Muss für den Werkzeugkasten eines jeden Händlers.
Algorithmen zur Optimierung mit Populationen: Shuffled Frog-Leaping Algorithmus (SFL) Algorithmen zur Optimierung mit Populationen: Shuffled Frog-Leaping Algorithmus (SFL)
Der Artikel enthält eine detaillierte Beschreibung des Shuffled-Frog-Leaping-Algorithmus (SFL) und seiner Fähigkeiten bei der Lösung von Optimierungsproblemen. Der SFL-Algorithmus ist vom Verhalten der Frösche in ihrer natürlichen Umgebung inspiriert und bietet einen neuen Ansatz zur Funktionsoptimierung. Der SFL-Algorithmus ist ein effizientes und flexibles Werkzeug, das eine Vielzahl von Datentypen verarbeiten und optimale Lösungen erzielen kann.
Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil I): Erzeugungsmuster Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil I): Erzeugungsmuster
Es gibt Methoden, mit denen sich viele Probleme lösen lassen, die sich ständig wiederholen. Wenn Sie einmal verstanden haben, wie man diese Methoden anwendet, kann es sehr hilfreich sein, Ihre Software effektiv zu erstellen und das Konzept von DRY (Do not Repeat Yourself) anzuwenden. In diesem Zusammenhang eignet sich das Thema Entwurfsmuster sehr gut, da es sich um Muster handelt, die Lösungen für gut beschriebene und wiederkehrende Probleme bieten.