Programmierung

Message Exchange (MSX-Befehle Teil 1)

Wie der Name Message Exchange (engl. “Nachrichten Austausch”) schon vermuten lässt, dienen die MSX-Befehle zum Austausch von Daten in einer individuell aufgebauten Nachricht. Mit Hilfe der MSX-Befehlen kann der Entwickler Datenkanäle öffnen und anschließend Nachrichten in beliebiger Anzahl und Größe darüber senden oder lesen. Dadurch wird der Datenaustausch über ein selbst definiertes Protokoll ermöglicht.


Die MSX-Befehle unterstützen den Datenaustausch in CONZEPT 16 über TCP/IP-Sockets, Pipes und externe Dateien. Die Kommunikation über Sockets und Pipes erfolgt dabei synchron, da nur dann eine Verbindung aufgebaut werden kann, wenn ein Empfänger Daten auf einen bestimmten Port erwartet. Die Kommunikation über eine externe Datei erfolgt asynchron. Ein gleichzeitiges Lesen und Schreiben ist nicht möglich. Dafür kann eine Datei geschrieben und erst zu einem späteren Zeitpunkt gelesen werden.

Aufbau einer Nachricht

Eine Nachricht setzt sich aus einer unbeschränkten Anzahl an Elementen zusammen, die wiederum mehrere Datenfelder enthalten. Diese Datenfelder sind nicht an einen speziellen Datentyp gebunden.

_MsxMessage

Eine MSX-Nachricht beginnt immer mit der Nachrichten-ID (_MsxMessage), diese dient als Kopf und ist nur einmal in jeder Nachricht vorhanden. Die Nachrichten-ID wird vom Benutzer definiert. Dies ermöglicht eine spätere Differenzierung des Nachrichtentyps.

_MsxItem

Der Nachrichten-ID (_MsxMessage) können beliebig viele Elemente (_MsxItem) untergeordnet werden, diese können zur Identifizierung des Dateiinhalts(_MsxData) benutzt werden. Auch hier werden die einzelnen Element-IDs vom Benutzer angegeben um bestimmte Daten von anderen trennen und später unterscheiden zu können. Die Element-ID darf sich innerhalb einer Nachricht wiederholen.

_MsxData

Den Elementen (_MsxItem) wiederrum können beliebig viele Dateibereiche (_MsxData) untergeordnet werden. Diese enthalten den eigentlichen Inhalt der Nachricht. Die Dateibereiche werden seriell geschrieben und gelesen.

_MsxEnd

Dieses Element stellt das Ende einer Nachricht dar.

Vorteil der MSX-Befehle

Nachrichten die per MSX-Befehle erstellt wurden sind binär kodiert, wodurch sie für den Menschen nicht lesbar sind. Außerdem besitzen MSX-Nachrichten einen nur sehr geringen Overhead. Dadurch dass einzelne Nachrichten mit unterschiedlichen IDs versehen werden können, ist es möglich unbekannte Nachrichtentypen zu ignorieren. Die untergeordneten Elemente kann der Entwickler ebenfalls mit IDs versehen, wodurch eine spätere Identifizierung gegeben ist. Diese kann dazu genutzt werden um unbekannte oder bereits übertragene Daten zu überspringen. Weiterhin können jederzeit neue Datenfelder hinzugefügt werden, ohne dass der Empfänger diese kennen muss. In diesem Fall werden die neuen Felder durch die Programmierung ignoriert und das nächste Element wird gelesen. Bei einer Erweiterung des Protokolls müssen daher nicht zwingendermaßen alle beteiligten Komponenten aktualisiert werden. Die MSX-Befehle ermöglichen somit nicht nur das Erstellen von abwärts-, sondern auch von aufwärtskompatiblen Protokollen.

Beispiel

Es existieren 2 Server, Server A und Server B. Diese Server kommunizieren miteinander über ein, von Ihnen mittels MSX-Befehlen entwickelten, Protokoll. Auf Server A haben Sie letztens eine Anpassung des Protokolls durchgenommen, welches das Element 3 beim Empfangen einer Nachricht berücksichtigt und beim Versenden erstellt.

Es wird eine wie folgt erstellte Nachricht von Server B an Server A versendet.

// Nachricht über Socket versenden
tSck # SckConnect('Server-A', 1000)
if (tSck > 0)
{
  // Nachrichtenkanal öffnen
  tMsx # MsxOpen(_MsxSocket | _MsxWrite, tSck);

  if (tMsx > 0)
  {
    // In den Nachrichtenkanal schreiben
    tMsx->MsxWrite(_MsxMessage,1);
    tMsx->MsxWrite(_MsxItem,1);
    tMsx->MsxWrite(_MsxData,'SU');
    tMsx->MsxWrite(_MsxItem,2);
    tMsx->MsxWrite(_MsxData,'DELETE');
    tMsx->MsxWrite(_MsxData,10);
    tMsx->MsxWrite(_MsxEnd,0);

    // Nachrichtenkanal schließen
    tMsx-MsxClose();
  }

  // Socket-Verbindung schließen
  tSck->SckClose();
}

Beispiel abwärtskompatibel

Server B hat keine Anpassung von Ihnen erhalten und hat daher kein Element 3 erstellt. Dennoch kann Server A an Hand der Element-ID die übrigen Elemente individuell verarbeiten. Ist kein weiteres Element vorhanden, ist die Nachricht abgeschlossen und die Verarbeitung kann damit beendet werden. Es wird nicht auf das Element 3 gewartet.

// Nachrichtenkanal lesend öffnen
tMsx # MsxOpen(_MsxSocket | _MsxRead,aSck);

if (tMsx > 0)
{
  // Erste Nachricht und erstes Element lesen
  tMsx->MsxRead(_MsxMessage,tMessageID);
  tMsx->MsxRead(_MsxItem,tItemID);

  // Alle Elemente lesen und an Hand der ID individuell verarbeiten
  while (tItemID > 0)
  {
    switch (tItemID)
    {
      case 1 :
      {
        tMsx->MsxRead(_MsxData,tUser);
        ...
      }

      case 2 :
      {
        tMsx->MsxRead(_MsxData,tCommand);
        tMsx->MsxRead(_MsxData,tRecordID);
        ...
      }

      case 3 :
      {
        tMsx->MsxRead(_MsxData, tExtraInfo);
      ...
      }

    }
    // Nächstes Element lesen
    tMsx->MsxRead(_MsxItem,tItemID);
  }
  // Nachrichtenende erreicht
  tMsx->MsxRead(_MsxEnd,0);

  // Nachrichtenkanal schließen
  tMsx->MsxClose();
}

Beispiel aufwärtskompatibel:

Dieses Mal schickt Server A eine Nachricht, die das Element 3 enthält an Server B. Wie oben schon erwähnt, benutzt Server B eine etwas ältere Version des Protokolls und kennt daher das Element nicht. Nun wird innerhalb einer Schleife ein Element nach dem Anderen abgearbeitet. Irgendwann kommt dann auch das Element, das Server B nicht kennt. Das ist nicht weiter schlimm, denn Server B erkennt, dass er das Element nicht kennt und es damit einfach überspringen kann. Die Verarbeitung wird mit dem nächsten Element fortgeführt.

// Nachrichtenkanal lesend öffnen
tMsx # MsxOpen(_MsxSocket | _MsxRead,aSck);

if (tMsx > 0)
{
  // Erste Nachricht und erstes Element lesen
  tMsx->MsxRead(_MsxMessage,tMessageID);
  tMsx->MsxRead(_MsxItem,tItemID);

  // Alle Elemente lesen und an Hand der ID individuell verarbeiten
  while (tItemID > 0)
  {
    switch (tItemID)
    {
      case 1 :
      {
        tMsx->MsxRead(_MsxData,tUser);
        ...
      }

      case 2 :
      {
        tMsx->MsxRead(_MsxData,tCommand);
        tMsx->MsxRead(_MsxData,tRecordID);
        ...
      }

      // default :
        // Do nothing...

    }
    // Nächstes Element lesen
    tMsx->MsxRead(_MsxItem,tItemID);

  }
  // Nachrichtenende erreicht
  tMsx->MsxRead(_MsxEnd,0);

  // Nachrichtenkanal schließen
  tMsx->MsxClose();
}

Auch bei uns intern finden die MSX-Befehle ihre Anwendung. Beispielsweise geschieht die Kommunikation zwischen Service-Prozess und Manager-Prozess über ein von uns mittels MSX-Befehlen realisierten Protokoll.

Im nächsten Teil werde ich etwas näher auf die Verwendung von MSX-Befehlen eingehen.

3 Kommentare

3 Kommentare “Message Exchange (MSX-Befehle Teil 1)”

  1. @Th.Eichle: In Patricks Beispielen sieht man ja, dass beim Lesen der Nachricht in jedem Schritt jedes Item möglich wäre. Dadurch ist die Kompatibilität auch dann gewährleistet, wenn das unbekannte Item irgendwann zwischendurch kommt. Demnach:
    1) Bei Abwärtskompatibilität: Ein erwartetes Item kommt nicht vor => dann wird der entspr. Case im Switch nie angesprungen.
    2) Bei Aufwärtskompatibilität: Ein unerwartetes Item kommt vor => dann wird einfach das nächste Item gelesen oder der Default-Zweig durchlaufen.

  2. d.h. der Empfänger muß immer wissen, wie der Befehl ungefähr aussieht.
    Gibt es eine Möglichkeit unbekannte oder defekte MSX-Befehle zu analysieren ?
    die Kompatibilität dürfte doch spätestens dann enden, wenn nicht am Ende eingefügt wurde.

  3. Die MSX-Befehle sind ein ausgesprochen nützliches Instrument. Ich habe sie bei einem Projekt für den zentralen asynchronen Datenaustausch über externe Dateien in Kombination mit dem Überwachen von externen Verzeichnissen (EvtFsiMonitor) verwendet.

    Zur Liste der Vorzüge dieser Nachrichtenaustauschmethode sollte man unbedingt hinzufügen, dass die C16-internen Datentypen *ohne Wandlung* ausgetauscht werden können, was ich als enormen Vorteil empfunden habe (ich habe z.B. Zeitdaten (Typ "Time" über diese Schnittstelle übermittelt).

    Allerdings: Aus der Tatsache, dass die Nachrichten binär kodiert sind kann man nicht unbedingt schliessen, sie wären nicht menschenlesbar.Strings stehen im Klartext im Zielformat enthalten und sollten daher verschlüsselt werden, falls die Nachricht über einen unsicheren Kanal übertragen wird und die übertragenen Informationen vertraulich sind.

Kommentar abgeben