Hintergrund-Informationen zu netBiDiB

Die Protokollspezifikation zu netBiDiB entstand aus der Absprache zwischen Steuerungsherstellern, Knotenentwicklern und Anwendern unter der Berücksichtigung und Abwägung der Belange Sicherheit, Anwenderfreundlichkeit, Funktionsumfang und Implementierungsaufwand. Auf dieser Seite sind Erläuterungen zu den Designentscheidungen sowie eine nähere Beleuchtung der Funktionalität als FAQ zu finden.

  1. Warum werden sowohl UDP als auch TCP verwendet?
  2. Warum wurde auf Verschlüsselung verzichtet?
  3. Warum wurde kein Standard-Protokoll für Service-Discovery verwendet?
  4. Wozu ist das Pairing notwendig?
  5. Wie wird die UID des Hosts gewählt?
  6. Wieso benötigt auch der Host eine UID fürs Pairing?
  7. Wie wird der Name im Descriptor eines Hosts gewählt?
  8. Der Pairing-Prozess ist viel zu kompliziert.
  9. Wie groß soll der Pairing-Timeout gewählt werden?
  10. Wie sieht der Pairing-Handschlag aus?
  11. Geht es nicht auch einfacher?
  12. Ist netBiDiB eine 1:1 oder eine 1:N-Verbindung?
  13. Warum muss ich auf die Anmeldung des Knoten warten?
  14. Wie behandelt das Interface den Anmelde-Vorgang?
  15. Wie sieht der Anmelde-Vorgang im Knoten aus?
  16. Wie kann ein Knoten zwischen Hosts wechseln?
  17. Wie sieht der Ablauf beim Wechsel zwischen Verbindungen aus?
  18. Warum werden bei LOGON/LOGOFF Adresse und UID mitgesendet?
  19. Wofür stehen die ganzen Abkürzungen?
  20. Wie wird der Datenstroms dekodiert?
  21. Wie sehen die Rohdaten der Beispiel-Anmeldevorgänge aus?

Warum werden sowohl UDP als auch TCP verwendet?

BiDiB hat das Ziel, eine fehlerfreie Kommunikation zur Modellbahn zu realisieren. Dies ist nicht nur für zuverlässigen Regelbetrieb sondern gerade auch bei Firmwareupdates wichtig. UDP hat keine Auslieferungsgarantie und auch keine garantierte Einhaltung der Reihenfolge, was insbesondere im Funkbetrieb via WLAN zu einer hohen Fehlerrate führt.

BiDiB hat zwar mit den Nachrichtensequenznummern bereits eine Ende-zu-Ende-Absicherung und wichtige Nachrichten werden durch automatische Wiederholung bei ausbleibender Bestätigung gesichert, jedoch ist dies für seltene Störungen vorgesehen und nicht für fehleranfällige Transportkanäle ausgelegt. Daher ist eine zusätzliche Sicherungsschicht auf der individuellen Transportebene nötig und vermindert auch den Aufwand einer applikationsspezifischen Absicherung.

Deshalb wählten wir (trotz höheren Speicher- und Prozessorleistungsbedarfs gerade auf kleineren Baugruppen) die Kommunikation via TCP. Andererseits bietet TCP keinen Broadcast, den wir für ein anwenderfreundliches automatisches Finden von Baugruppen benötigen.

Also wurde im Discovery-Schritt UDP verwendet, die restliche Kommunikation geht dann über TCP.

Warum wurde auf Verschlüsselung verzichtet?

Eine Kommunikation im Internet (insbesondere, wenn dabei auch ein Firmwareupdate möglich ist) muss aus heutiger Sicht zwingend verschlüsselt sein. Allerdings bedeutet eine sichere Implementierung erheblichen Aufwand und ein umfassendes Sicherheitskonzept, das bei Abdeckung aller Aspekte an vielen Stellen eine Einschränkung bedeutet.

Eine Modellbahnsteuerung wird jedoch in der Regel im abgesicherten lokalen Heimnetzwerk betrieben, es findet kein Datenverkehr durch das Internet statt. Wo dies erforderlich ist, kann ein Virtual Private Network (VPN) den nötigen Schutz bieten.

Auch wenn der Einfachheit halber zurzeit keine Verschlüsselung erforderlich ist, wurde die Architektur bereits so angelegt dass sie auch mit abgesicherten Verbindungen verwendet werden kann.

Warum wurde kein Standard-Protokoll für Service-Discovery verwendet?

Existierende Service-Discovery-Protokolle wurden in Betracht gezogen. Alle konnten jedoch unsere Anforderungen für eine einfache Verwendung nicht erfüllen. So soll das Protokoll auch auf Knoten mit geringen Prozessorressourcen unterstützt werden können, und in Anwendungen leicht zu implementieren sein ohne auf externe Bibliotheken oder Betriebssystem-Services zurückgreifen zu müssen, die nicht überall zur Verfügung stehen.

Daher haben wir ein naives Protokoll entwickelt, das mit entsprechenden Binärnachrichten genau auf unseren Anwendungsfall zugeschnitten ist und für die erwarteten Einsatzszenarien gerade ausreichend ist. Die Schwächen liegen auf der Hand: schlechte Skalierbarkeit in Netzwerken mit vielen Knoten, Verzicht auf Fehlerbehandlung, keine Berücksichtigung des Lastverhaltens, Benutzung eines festen nicht reservierten Ports, fehlende Interoperabilität.

Es handelt sich bei Discovery um ein optionales Komfortfeature. Die Implementierung ist empfohlen, aber nicht verpflichtend, und auch nicht exklusiv: ein Service kann parallel über mehrere Protokolle bekanntgemacht werden. Dafür bieten sich folgende Protokolle an, die bei vertretbarem Aufwand von netBiDiB-Teilnehmer benutzt werden sollen:

Bei der Bezeichnung von Services mit einem URI soll bidib:// als Scheme verwendet werden.

Wozu ist das Pairing notwendig?

Ein netBiDiB-System soll sich 'von selbst' verbinden können, aber nur mit den vom Anwender gewünschten Geräten. Dies soll auch dann funktionieren, wenn sich mehrere unabhängige netBiDiB-Systeme im selben Netz befinden, was z. B. auf Modellbahnertreffen oder in Vereinen mit mehreren Anlagen(teilen) der Fall sein kann.

Wer soll sich nun wohin verbinden? Mit dem Pairing haben wir eine komfortable Möglichkeit geschaffen, auch in solchen Umgebungen den 'eigenen' Anlagenteil zusammenzuhalten und zugleich die automatische Verbindung zuzulassen.

Wieso benötigt auch der Host eine UID fürs Pairing?

Beim Pairing merkt sich nicht nur der Host die verbundenen Knoten, sondern auch die Knoten merken sich ihrerseits den bevorzugten Verbindungspartner. So wird unter anderem verhindert, dass ein handelsübliches Hostprogramm zur 'feindlichen Übernahme' eines Systems verwendet werden kann - weder versehentlich noch absichtlich. Die verschiedenen Programminstanzen benötigen dazu natürlich ein Unterscheidungsmerkmal, die Verwendung der Unique-ID-Infrastruktur von BiDiB war naheliegend.

Wie wird die UID des Hosts gewählt?

Die Unique-ID einer Hostprogramm-Instanz soll möglichst global eindeutig sein, dies lässt sich aber nicht ohne eine zentrale Vergabestelle realisieren. Eine derartige Infrastruktur ist jedoch unverhältnismäßig aufwändig, wo sie nicht bereits vorhanden ist (etwa für die Vergabe von Programmlizenzen) soll daher auf einen Zufallsgenerator ausgewichen werden. Eine zufällig generierte Seriennummer wird entweder in den Programmeinstellungen oder in der Anlagendatei gespeichert, um sich nicht bei jedem Programmstart zu ändern. Alternativ kann die Seriennummer auch mittels einer Hashfunktion aus einer eindeutigen Kennung des Computers (z.B. MAC-Adresse) erzeugt werden.

Wie bei Knoten beginnt die Unique-ID mit einer Herstellerkennung (Vendor ID), jeder Hersteller kann über seinen Nummernraum frei verfügen und eine entsprechende Vergabemethode wählen. Programmanbieter ohne NMRA-Kennung können eine 16-Bit Produktkennung aus dem Bereich für Open-Source-Komponenten (VID 13) erhalten.

Wie wird der Name im Descriptor eines Hosts gewählt?

Der Descriptor dient dem Anwender zur leichten Unterscheidung mehrerer Verbindungen, ohne nur die UID zum Abgleich verwenden zu können. Auf einem Gerät mit GUI, z. B. einem Handregler oder Konfigtool, kann der Anwender etwa auswählen an welchem von mehreren verfügbaren Hosts sich der Knoten anmelden soll. Der Produkt- und Teilnehmername sollten daher möglich aussagekräftig sein.

Als Produktname soll der Programmname verwendet werden, der Teilnehmername kann entweder vom Anwender direkt einstellbar sein oder automatisch aus Computernamen, Betriebssystem-Login oder dem Namen der geöffneten Anlagendatei erzeugt werden.

Der Pairing-Prozess ist viel zu kompliziert.

Der Linkstatus kann durch einen einfachen asynchronen Zustandsautomaten repräsentiert werden. Dieser hat nur 5 Zustände:

  • none: der Link existiert noch nicht (wurde noch nicht initialisiert), das Gegenüber ist nicht bekannt
  • unpaired: beide Seiten vertrauen sich nicht (oder wissen es nicht)
  • their-accept: die Gegenseite vertraut dem Teilnehmer (hat zuletzt STATUS_PAIRED gesendet), umgekehrt aber nicht
  • my-accept: der Teilnehmer vertraut der Gegenseite (hat zuletzt STATUS_PAIRED gesendet), umgekehrt aber nicht (bzw. es ist noch unbekannt, es wird auf eine Antwort gewartet)
  • paired: beide Linkpartner vertrauen einander (haben jeweils zuletzt STATUS_PAIRED gesendet)

Der Ablauf des Pairings wird durch einen zweiten asynchronen Zustandsautomaten mit nur 3 Zuständen geregelt:

  • idle: es findet gerade kein Pairing-Vorgang statt
  • my-request: der Teilnehmer hat einen Antrag gesendet und wartet auf Erwiderung des Handschlags (bzw. auf das Ende des Pairingprozesses)
  • their-request: der Teilnehmer hat einen Antrag bekommen und wartet auf den Anwender (bzw. auf das Ende des Pairingprozesses)

Ein Pairingvorgang endet dabei mit Erreichen des Linkstatus paired, mit Ablauf des Timers, bei Ablehnung/Abbruch seitens des Anwenders, oder bei Ablehnung/Abbruch durch die Gegenseite (unaufgefordertes STATUS_PAIRED/STATUS_UNPAIRED).

Zustandsübergänge
EreignisReaktion für LinkReaktion für Pairingprozess
TCP-ACK other := null link := none send(DESCRIPTOR_UID, my_uid)
TCP-FIN other := null link := none pairing := idle
receive(DESCRIPTOR_UID, their_uid) other := their_uid if known_trusted(other): send(STATUS_PAIRED) link := my-accept else: send(STATUS_UNPAIRED) link := unpaired
on_paired() store_trust(other) pairing := idle
receive(STATUS_PAIRED) if link == unpaired: link := their-accept if link == my-accept: on_paired() link := paired
receive(STATUS_UNPAIRED) if link != none: remove_trust(other) link := unpaired pairing := idle
on_requests_exchange_complete() if link != none: send(STATUS_PAIRED) if link == unpaired: link := my-accept if link == their-accept: on_paired() link := paired
receive(PAIRING_REQUEST) if pairing == my-request: on_requests_exchange_complete() if pairing == idle: if link != none: pairing := their-request
Anwender leitet Pairing ein if pairing == idle: send(PAIRING_REQUEST) pairing := my-request
Anwender akzeptiert Pairing if pairing == their-request: send(PAIRING_REQUEST) on_requests_exchange_complete()
Anwender lehnt Pairing ab
oder löst es auf
if link != none: remove_trust(other) send(STATUS_UNPAIRED) link := unpaired pairing := idle
Anwender (oder Timeout)
bricht Pairing ab
if known_trusted(other): send(STATUS_PAIRED) else: send(STATUS_UNPAIRED) pairing := idle

Zustandsdiagramme mit den Übergängen für beide Automaten. Gestrichelt dargestellt: Übergang aus beliebigem Zustand.

Kleine Abweichungen von diesem einfachen Modell sind in ausgefeilteren Implementierungen möglich:

  • Beim Empfang von STATUS_UNPAIRED braucht remove_trust(other) nicht aufgerufen zu werden wenn es dem Anwender auch am Gerät möglich ist ein indivuelles Pairing zurückzunehmen. Der Linkzustand kann dann von paired/my-accept zu my-accept übergehen (statt ganz bis unpaired).
  • Beim Zurücknehmen eines Pairings (Senden von STATUS_UNPAIRED) kann der Linkzustand von paired zu their-accept übergehen (statt ganz bis unpaired).
  • Beim Empfang eines PAIRING_REQUEST in den Zuständen paired oder my-accept kann STATUS_PAIRED sofort gesendet werden (nicht erst nachdem der Anwender den Antrag annimmt oder der Timeout abläuft).
  • Beim Empfang eines PAIRING_REQUEST im Zustand paired kann der Pairing-Prozess im Zustand idle verbleiben (und keinen Anwender-Dialog anzeigen, sondern nur sofort mit STATUS_PAIRED antworten).
  • Wenn der Anwender einen Pairing-Antrag annimmt (im Zustand their-request), können die Nachrichten mit PAIRING_REQUEST und STATUS_PAIRED in beliebiger Reihenfolge gesendet werden.
  • Es wird davon ausgegangen, dass ein Anwender den Pairing-Vorgang (nur) in den Zuständen unpaired, my-accept und their-accept einleiten kann. Um einen "Pairing-Modus" zu implementieren, der auch aus dem Zustand none oder vor dem TCP-Verbindungsaufbau betreten werden kann, darf der PAIRING_REQUEST nicht gesendet werden bevor DESCRIPTOR_UID emfangen wurde und der Zustand unpaired bzw. my-accept erreicht wurde.
  • Wenn der PAIRING_REQUEST-Handschlag in den Zuständen my-request/their-request erfolgt, braucht der Prozess-Automat nicht im jeweiligen Zustand zu verbleiben sondern kann auch als Zwischenschritt in einen vierten Zustand both-requests übergehen, in welchem etwa der Anwender den Antrag nicht erneut annehmen kann.

Auch Geräte, die nur einen Pairing-Knopf und damit keine Möglichkeit zur Unterscheidung mehrerer Links haben, verwenden dieselben universellen Zustandsautomaten auf jedem der Links. Die vom Anwender (bzw. Timeout) ausgelösten Events wirken dabei prinzipiell auf alle Links gleichzeitig. Dennoch müssen gewisse Überlappungen besonders berücksichtigt werden:

  • Ein Knopfdruck zur Initiierung eines Pairings sendet etwa auf jedem Link einen PAIRING_REQUEST, sodass alle Links in den Pairing-Zustand my-request übergehen. Wird nun einer dieser Anträge akzeptiert, sollen die PAIRING_REQUESTs auf den anderen Links zurückgenommen werden (wie "Abbruch durch Timeout").
  • Werden mehrere dieser Anträge in schneller Abfolge (innerhalb des ursprünglichen Timeouts) angenommen, so ist etwas schiefgelaufen und statt nach "The winner takes it all" zu verfahren sollen alle eingegangenen PAIRING_REQUESTs nachträglich abgelehnt werden (wie "Pairing auflösen").
  • Wird ein weiterer PAIRING_REQUESTs empfangen, während ein anderer Link bereits im Pairing-Zustand their-request ist, sollen beide zurückgewiesen werden (wie "Ablehnung durch Anwender").

Wie groß soll der Pairing-Timeout gewählt werden?

Standardmäßig soll ein Timeout von etwa 30 bis 60 Sekunden verwendet werden. Bei Teilnehmern, die typischerweise stationär und schwer zugänglich installiert werden, kann auch das Doppelte (120s) veranschlagt werden. Bei mobilen Teilnehmern, die vom Anwender direkt zum Pairing-Ziel mit hingenommen werden können, kann der Timeout auch nur 5 bis 10 Sekunden betragen.

Wie sieht der Pairing-Handschlag aus?

Durch Ausmultiplizieren der beiden Zustandsautomaten von oben ergibt sich folgendes Zustandsdiagramm für den Übergang von unpaired-idle zu paired-idle:

Gut zu erkennen ist der von reihenfolgen-unabhängige 3-Wege-Handschlag. Er besteht aus einem Austausch von zwei PAIRING_REQUESTs und einem Austausch von zwei STATUS_PAIRED.

Geht es nicht auch einfacher?

Natürlich. Die dargestellten Automaten sind vor allem für interaktive Programme gedacht, die dem Anwender komfortabel den Systemzustand visualisieren und ihn in die einzelnen Schritte des Verbindungsaufbaus eingreifen lassen. Insbesondere bilden sie aber auch die vollständige Behandlung aller lokalen Nachrichten auf Serverseite ab, so dass keine Verbindung in einen ungültigen oder ungewissen Zustand kommen kann.

Möchte man nur möglichst einfach eine TCP-Verbindung für einen bereits zuvor gepairten Link aufbauen und einen Knoten steuern, so geht dies auch in einem Schritt ohne Nutzerinteraktion:


-connect(),send(EMITTER)->
-recv(EMITTER)/send(DESCRIPTOR_UID)...->     -recv(garbage)-> not netBiDiB
-recv(DESCRIPTOR_UID)/send(STATUS_PAIRED)->  -recv(DESCRIPTOR_UID(other)) /\ unknown(other)-> wrong node
-recv(STATUS_PAIRED)->                       -recv(STATUS_UNPAIRED)-> pairing required
-recv(LOCAL_LOGON)/send(LOCAL_LOGON_ACK)->   -recv(LOGOFF)-> not available
...
-send(LOCAL_LOGON_REJECTED),close()->

Diese Prozedur kann ausgeführt werden, wenn der Anwender auf den "Schnittstelle verbinden"-Button klickt - wenn es nicht klappt bekommt er eine Fehlermeldung und muss es nochmal probieren. Separat davon wird eine Prozedur zum Pairing angeboten, etwa im Einstellungsdialog des Digitalsystems wo auch IP-Adresse und Port des Servers angegeben werden:

// TODO
// this is a work in progress
-connect(),send(EMITTER)->
-recv(EMITTER)/send(DESCRIPTOR_UID)...->
-recv(DESCRIPTOR_UID)/send(PAIRING_REQUEST)->
-recv(STATUS_PAIRED)->                        -recv(PAIRING_REQUEST)/send(STATUS_PAIRED)->
-recv(PAIRING_REQUEST)/send(STATUS_PAIRED)->  -recv(STATUS_PAIRED)->
-send(LOCAL_LOGON_REJECTED),close()>                                                   -.-.-timeout->

Das ist natürlich sehr eingeschränkt, ermöglicht kein von Knotenseite initiiertes Pairing und behandelt auch kein STATUS_UNPAIRED. Besser wäre es, bei Eingabe der Adresse den Link aufzubauen und den Deskriptor der Gegenseite anzuzeigen, sowie erst dann auf Knopfdruck den Pairing-Handshake durchzuführen. Die Pairing-Möglichkeit darf gerne im Einstellungsdialog verbleiben, im regulären Betrieb muss dann auf PAIRING_REQUESTs keine Rücksicht genommen werden.

Ist netBiDiB eine 1:1 oder eine 1:N-Verbindung?

Beide Anwendungsfälle werden durch netBiDiB abgedeckt. Prinzipiell entscheidet

  • ein Client, zu wie vielen Servern er einen Link aufbaut
  • ein Server, von vielen Clients er (gleichzeitig) Links akzeptiert
  • ein Knoten, an welchem Interface er sich anmeldet (maximal eines)
  • ein Interface, wie viele (gleichzeitige) Anmeldungen von Knoten es akzeptiert (maximal 255)

Ein Server soll dabei so viele Links verwalten wie seine Ressourcen es zulassen. Ein Client (Interface, Host) kann entscheiden ob er nur einzelne Verbindungen oder beliebig viele nutzt. Aus Anwendersicht sind jedoch mehrfache Verbindungsmöglichkeiten zu bevorzugen, insbesondere bei der Implementation von Discovery ist dies unumgänglich für das Einlesen der Descriptoren aller Links.

Die Oberfläche eines Hostprogramms, das mehrere Verbindungen nutzen kann, könnte beispielsweise so aussehen:

Warum muss ich auf die Anmeldung des Knoten warten?

Bei einem physischen Bus wird ein Knoten durch das Anstecken fest einem bestimmten Interface zugewiesen. Die Anmeldeprozedur dient hier nur der Vereinzelung, Identifikation und Resourcenzuweisung. Bei einem rein virtuellen Bus mit parallelen TCP-Verbindungen ist diese Zuteilung zu einem einzelnen Interfaces nicht gegeben, das 'An- und Abstecken' muss explizit in der Software modelliert werden.

Ein Ansatz wäre der Verzicht auf parallel geöffnete Verbindungen. Der Knoten stellt nur einen Socket bereit, ist dieser belegt werden andere Verbindungen abgewiesen. Das aktive Programm muss erst die Verbindung schließen, bevor ein anderes aktiv werden kann.

Dies empfanden wir jedoch als ungenügend. So kann bei einem gescheiterten Verbindungsaufbau nicht unterschieden werden, ob der Server nicht erreichbar ist (etwa wegen falsch eingegebener Adresse) oder ob der Knoten bloß gerade nicht verfügbar (da bereits anderweitig verbunden) ist.

Darüberhinaus fehlt es an Informationen zum Systemzustand, die gerade in einem Mehrbenutzerszenario für die nötige Transparenz sorgen. Um welchen Knoten handelt es sich überhaupt? An welchem Interface ist er gerade angemeldet ist - wo müsste man ihn "abstecken"? Kennt mich der Knoten bereits oder müsste erst ein Pairing durchgeführt werden?

Um diese Auskünfte zu ermöglichen, ist die Anmeldung vom Verbindungsaufbau (und auch vom Pairing) separiert. Dieser "Beobachtungsmodus" ermöglicht Programmen eine nutzerfreundliche Ausgestaltung ihrer Oberfläche, der Link kann bis zu diesem Punkt auch automatisch aufgebaut werden noch bevor der Anwender die Kontrolle über den Knoten übernehmen möchte. Diese Zwischenstufe kann beim einfachen Verbindungsaufbau natürlich übersprungen werden, eine Nichtverfügbarkeit des Knotens (Nicht-Anmeldung, MSG_LOCAL_LOGOFF) wird dabei als Fehler betrachtet.

Auch nach der Abmeldung des Knotens kann ein Programm den Link aufrechterhalten und den Knotenstatus beobachten sowie zur Wiederanmeldung zur Verfügung zu stehen, was den Wechsel zwischen Programmen vereinfacht. Das "Abstecken" kann sowohl durch den Knoten mit MSG_LOCAL_LOGOFF als auch durch das Interface mit MSG_LOCAL_LOGON_REJECTED erfolgen.

Wie behandelt das Interface den Anmelde-Vorgang?

Direkt nach dem Erreichen des Link-Zustands paired sendet der Knoten entweder MSG_LOCAL_LOGON oder - wenn er bereits anderswo angemeldet ist - MSG_LOCAL_LOGOFF. Zum Übernehmen der Kontrolle braucht man den Logon nur noch mit MSG_LOCAL_LOGON_ACK zu akzeptieren.

Ein Programm, das den Link (vorerst) nur zum Beobachten des Knotenstatus geöffnet hat, lehnt den Logon mit MSG_LOCAL_LOGON_REJECTED ab. Aus dem abgemeldeten Zustand kann der Knoten mit einem STATUS_PAIRED aufgefordert werden, sich erneut anzumelden wenn er gerade verfügbar ist.

Der Empfang von MSG_LOCAL_LOGOFF (mit Sternchen markiert) muss nicht MSG_LOGON_REJECTED quittiert werden, verkehrt ist es aber auch nicht.

Wie sieht der Anmelde-Vorgang im Knoten aus?

Ein Knoten, der Verbindungen von mehreren Interfaces (Clients) entgegennehmen kann, benötigt eine Sperre zum wechselseitigen Ausschluss (mutual exclusion semaphore, Mutex Lock) um sicherzustellen dass er sich nur an einem Interface gleichzeitg anmelden wird. Sein Verhalten kann mit einem Zustandsautomaten je Verbindung beschrieben werden. Der Mutex stellt dabei sicher, dass sich entweder alle Links im Zustand logged-off befinden oder genau ein Link in logon-attempt/logged-on und alle anderen in logon-elsewhere.

Der Knoten sucht sich aus bei welchem Interface er sich anmelden möchte. Stehen mehrere zur Auswahl, werden sie der Reihe nach durchprobiert, solange bis eines von ihnen die Anmeldung akzeptiert hat oder alle sie verweigert haben. Die Prioritäten dabei legt der Knoten fest, und bevorzugt etwa frische Pairings, oder das in der letzten Session benutzte Interface. Ein Knoten mit eigener GUI kann die Auswahl selbstverständlich auch dem Anwender überlassen. Genauso denkbar ist ein konfigurierbares oder hartkodiertes Ranking.

Die Anmeldung erfolgt grundsätzlich auf Initiative des Knotens, es ist erlaubt sie auch jederzeit abbrechen wenn sich seine Prioritäten ändern, zum Beispiel beim Linkaufbau durch ein höher priorisiertes Interface. Befindet sich ein Link (und alle anderen) im Zustand logged-off, so sollte der Knoten jeder Anmeldeaufforderung (STATUS_PAIRED) entsprechen. Von den im Diagramm dargestellten Aktionen und Reaktionen kann an den mit Sternchen markierten Stellen abgewichen werden:

  • Empfang von STATUS_PAIRED im Zustand logged-off: In Ausnahmefällen darf die Anmeldeaufforderung auch mit LOCAL_LOGOFF abgelehnt werden.
  • Der Logoff aus den Zuständen logon-attempt und logged-on kann nicht nur bei Ablauf des Timers oder Empfang von LOCAL_LOGON_REJECTED erfolgen, sondern jederzeit (etwa zum Neustart beim Firmwareupdate oder bei einer Herabstufung der Linkpriorität).

Ist nur ein einziger Link aktiv, darf der Knoten bei Empfang von LOCAL_LOGON_REJECTED auch im Zustand logon-attempt verbleiben (bzw. vom Zustand logged-on dorthin zurückwechseln), ohne den Mutex freizugeben und LOCAL_LOGOFF zu senden. Dies ermöglicht die sofortige Wiederaufnahme der Anmeldung durch LOCAL_LOGON_ACK, ist in der Regel aber unnötig.

Wie kann ein Knoten zwischen Hosts wechseln?

An einem physischen Bus entscheidet der Anwender durch das Anstecken der Leitung, an welchem Interface sich der Knoten anmelden soll. Bei netBiDiB können mehrere Links gleichzeitig offen sein, das 'Umstecken' wird daher mittels An-/Abmeldung explizit in der Software modelliert. Zum Wechsel zwischen zwei Programmen wird der Anwender im gerade aktiven Programm den Knoten abmelden und dann im anderen Programm die Kontrolle über den Knoten anfordern.

Die Abmeldung erfolgt mittels MSG_LOCAL_LOGON_REJECTED von seiten des Interfaces, selten auch durch den Knoten mit MSG_LOCAL_LOGOFF. Dabei wird nur die Anmeldung aufgehoben, der Link bleibt offen (paired) und steht für die Statusbeobachtung oder eine Wiederanmeldung zur Verfügung. Ein einfacher Client kann die TCP-Verbindung auch ganz trennen.

Der abgemeldete Knoten wählt nun automatisch ein anderes Interface aus, mit dem er gepaired ist (d.h. welches verbunden und vertrauenswürdig ist), und versucht sich dort anzumelden. Von allen Clients, die sich nur im Beobachtungsmodus befinden (Interface-Zustand logged-off), wird er zunächst abgewiesen.

Im ausgewählten Programm will der Nutzer nun die Kontrolle über den abgemeldeten Knoten übernehmen. Dieses sendet eine Anmelde-Aufforderung (STATUS_PAIRED) an den Knoten und nimmt nun einen LOGON mittels LOGON_ACK an. Auch ein erneutes Pairing (Austausch von zwei PAIRING_REQUESTs) führt zum selben Ergebnis und kann von Interfaces ohne GUI für diesen Zweck genutzt werden.

Ein Hin- und Herwechseln zwischen zwei Programmen durch einen einzigen Klick ist möglich, wenn .

Um einen beliebigen Wechsel zwischen mehr als zwei Programmen zu ermöglichen, wird ein LOGON_ACK als Einladung zur Anmeldung verstanden. gibt dem jeweiligen Interface die höchste Priorität bei der nächsten Anmeldung. Sobald die bestehende Verbindung freigegeben wird, wechselt der Knoten zu dem priorisierten Interface.

Wie sieht der Ablauf beim Wechsel zwischen Verbindungen aus?

Warum werden bei LOGON/LOGOFF Adresse und UID mitgesendet?

Es sollte der Einfachkeit halber dasselbe Nachrichtenformat genutzt werden wie auf dem BiDiBus, auch wenn dies eigentlich nicht notwendig ist. Die Unique-ID ist den Teilnehmern bereits vor dem Anmeldevorgang bekannt (per MSG_LOCAL_LINK DESCRIPTOR_UID), die Knotenadresse ist auf einzelnen netBiDiB-Verbindungen irrelevant da kein geteiltes 'Busmedium' genutzt wird.

Die redundante Verwendung der UID bietet auch eine Absicherungsmöglichkeit gegen fehlerhafte Implementierungen.

Wie wird der Datenstroms dekodiert?

Wofür stehen die ganzen Abkürzungen?

Wie sehen die Rohdaten der Beispiel-Anmeldevorgänge aus?

Inhalt des UDP-Pakets Der TCP-Strom...