SSL/TLS-Netzwerkprogrammierung mit Boost.Asio, Teil 3: Client-Programmierung und Fehlerbehandlung

Seite 4: POSIX und Fazit

Inhaltsverzeichnis

Die Architektur des POSIX-Clients befreit von den beiden Einschränkungen des portablen. Das Absetzen mehrerer Kommandos unabhängig vom Abschluss der Verarbeitung auf dem Server ist ohne Weiteres möglich. Schließlich erfolgt das Lesen vom Socket in einer unabhängigen Operation. Damit kann es passieren, dass Ausgaben vom Server ankommen, während die Eingabe auf dem Terminal noch in vollem Gange ist. Der POSIX-Client reagiert wie ein Unix-Terminal, bei dem von Hintergrundprozessen durchaus Ausgaben in den aktuellen Eingabeprozess einfließen.

Es ist ebenfalls möglich, bei laufender Eingabe einen Verbindungsabbruch zum Server festzustellen. Selbst wenn der asynchrone Lesevorgang von STDIN noch nicht im Handler angelangt ist, kann die Leseprozedur vom Socket zum Server bereits einen Verbindungsabbruch erkennen. Die Fehlerbehandlung im Handler listen_on_server() kann dann das Programm aus dem Hintergrund heraus beenden.

Entwickler können das Verhalten mit folgenden Schritten ausprobieren:

  • Zunächst starten sie einen WOPR-Server. Idealerweise mit simulierter langer Verarbeitung (Option -d).
  • Anschließend verbinden sie jeweils sterminal und terminal mit dem Server. Ein anschließender Logon als Joshua ist ideal.

Nun können die Entwickler in terminal mehrere Kommandos hintereinander absetzen. Die mehrfache Eingabe von Poker ist beispielsweise problemlos möglich, ohne die Ausgabe des Servers abzuwarten. In sterminal hingegen können sie nur ein Kommando eingeben, auf die Ausgabe warten und anschließend das nächste Kommando eingeben.

Nach dem Beenden des WOPR-Servers durch Ctrl-C reagiert terminal prompt und hält an. sterminal hingegen bleibt hängen, falls es gerade auf ein neues Kommando von STDIN wartet.

POSIX selbst eröffnet eine breite Palette von Plattformen. Boost.Asio bietet mit seinen speziellen POSIX-Funktionen interessante zusätzliche Möglichkeiten zu den plattformunabhängigen. Wer jedoch glaubt, das Entwickeln und Testen auf einer POSIX-Plattform reicht, um alle POSIX-konformen Systeme identisch abzudecken, irrt gewaltig.

Im Konstruktor von WoprTerminal fällt folgendes Präprozessorkonstrukt auf:

#ifdef sun
input_(io_service, (isatty(fileno(stdin)) ? ::open("/dev/tty",
O_RDONLY | O_NONBLOCK) : ::dup(STDIN_FILENO))),
#else
input_(io_service, ::dup(STDIN_FILENO)),
#endif

Der #else-Teil ist die typische POSIX-Variante mit dup() zum Duplizieren von STDIN. Die #ifdef-Klausel testet, ob das Zielsystem Solaris ist. Ein einfaches dup() findet nur statt, sofern STDIN kein Terminal ist. Das ist der Fall, wenn über Eingabeumlenkung eine Pipe oder eine Datei an STDIN gebunden ist. Ist STDIN hingegen ein Terminal, werden das zugehörige TTY geöffnet und die Eingaben direkt davon gelesen.

Das ist notwendig, da Boost.Asio mit Solaris offensichtlich ein paar Schwierigkeiten hat. Per Standard nutzt Boost.Asio auf dieser Plattform /dev/poll, um auf asynchrone Ereignisse zu testen. Das hat beim klassischen ::dup(STDIN_FILENO) einen eigenartigen Effekt: Beim Start von terminal beendet es sich sofort wieder. Darauf beendet sich die Shell, die terminal startete, weil sie offensichtlich ein EOF auf STDIN erhielt.

Dieser Effekt lässt sich mildern: Wenn beim Kompilieren des Sourcecodes das Makro BOOST_ASIO_DISABLE_DEV_POLL gesetzt ist, nutzt Boost.Asio nicht mehr /dev/poll, sondern fällt auf select() zurück. Damit startet terminal und lässt sich auch nutzen. Wird terminal jedoch beendet, schließt die zugehörige Shell ebenfalls.

Entwickler können das umgehen, indem sie statt STDIN das Terminal /dev/tty öffnen. Ein-/Ausgabeumlenkung lässt sich über das TTY jedoch nicht nutzen, sondern nur über STDIN. Das ist der Grund für die Fallunterscheidung beim Instanziieren von input_.

An der Stelle zeigen sich die Grenzen der Plattformunabhängigkeit von Boost.Asio. Soll ein Programm später auf mehreren Plattformen lauffähig sein, sollte es auf den Zielsystemen ausführlich getestet werden. Das gilt nicht nur bei POSIX.

Der POSIX-Client bietet interessante Möglichkeiten. Perfekt wäre es, dieselbe Funktionsweise ebenso auf dem reinen Windows ohne Cygwin nutzen zu können. Ein Ansatz wäre boost::asio::windows::stream_handle statt der POSIX-Variante zu verwenden. Ob sich damit eine Lösung implementieren lässt, sei dem Leser als Übung überlassen.

Boost.Asio bietet einen einfachen und portablen Weg, SSL/TLS-Programme zu implementieren. Im Vergleich zum reinen OpenSSL-Programm aus der ursprünglichen Artikelreihe lässt sich die Palette unterstützter Plattformen ohne große Änderung am Quelltext und ohne exzessive Fallunterscheidung leicht erweitern.

Auch wenn Boost.Asio vieles kapselt und die portable Programmierung vereinfacht, verstecken sich die Herausforderungen im Detail. Die Terminal-Probleme auf Solaris – einem ausgewiesen POSIX-kompatiblen System – zeigen, dass blindes Vertrauen in die POSIX-Kompatibilität fatal sein kann. Intensives Testen ist auf allen angestrebten Plattformen unerlässlich.

Oliver Müller
ist freiberuflicher IT-Berater und -Trainer mit den Schwerpunkten Software Engineering und Kryptographie. Er berät zu Java EE, Unix/Linux, Android und Mainframe-Systemen.
(rme)