% Content-encoding: UTF-8 \documentclass[ngerman]{article} \usepackage[T1]{fontenc} \usepackage[latin1]{inputenc} \setcounter{secnumdepth}{0} \setcounter{tocdepth}{0} \renewcommand{\reftextbefore}{auf der vorherigen Seite} \renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite} \renewcommand{\reftextafter}{auf der nächsten Seite} \renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite} \renewcommand{\figurename}{Abbildung} \begin{document} \title{Leitungen und Stecker --- \\\hfill Sockets und Pipes in Forth} \ifx\shorttitle\undefined\else \shorttitle{Sockets und Pipes in Forth} \fi \author{Marcel Hendrix} \maketitle \section{Vorwort} Hast du dich schon mal gefragt, wie du andere Programme auf deinem Computer steuern kannst? Oder wie Programme auf verschiedenen Rechner das untereinander bewerkstelligen --- also im lokalen Netz oder im Internet? Dann stößt du auf das, was man eine API (application programming interface) nennt. Fragt sich nur, wie man von Forth aus solche APIs anderer Programme erreicht. Freundlicherweise ist diese Kommunikation zwischen den Prozessen inzwischen textbasiert gelöst. Es werden Anweisungen oder Skripte ausgetauscht. Und dazu haben diese Prozesse (Programme) Stellen, an denen man sich bildlich gesprochen \emph{anstöpseln} kann. Eine einführende Erklärung dazu habe ich bei Marcel Hendix zum iForth gefunden. Sein englischer Text ist hier übersetzt ins Deutsche wiedergegeben, und wurde von mir um ein erklärendes Glossar ergänzt. Ihr werdet sehen, dass seitdem der Beitrag verfasst wurde, einige Jahre vergangen sind. Doch da Grundlagen behandelt werden, ist das Thema weiterhin aktuell. Inzwischen gibt es Windows XP und Windows Vista, und Windows 7 ist angekündigt. Linux ist fortgeschritten, und es gibt Mac OS X mit Intelprozessoren. All diese sind hier nicht extra erwähnt, der Code läuft aber auf diesen Plattformen mit iForth 3.0, final 32--bit release, datiert vom 17.~Mai 2008. Es ist erhältlich für Windows, Linux x86\_32 oder x86\_64, und Mac OS X (Intel hardware only). iForth wird derzeit für 100 € vertrieben und unterstützt. Es bietet drei Fenster: Die klassische Konsole, dann einen RTF--Editor, und den \emph{graphics terminal screen} in OpenGL--Grafik (sehr schön!). Dazu eine Erweiterung um parallel processing, und ein Metacompiler, der schnelle und plattformunabhängige 32-Bit--Forth--Systeme erzeugt. Unter Linux und Windows gibt es Unterstüzung für (named) pipes, dynamic linking, Tcl/Tk, (X-)windows, SVGA graphics und full digital sound. Tcl/Tk arbeitet spektakulär gut zusammen mit iForth unter Windows. Diese I/O--Funktionalität wird durch ein Server--Programm --- den iServer --- erreicht, das in C geschrieben ist und mitgeliefert wird. Womit wir wieder bei den Steckern wären...\hspace*{\fill}{M.\ Kalus} \begin{multicols}{2} \section{Die Stecker} Hier eine \emph{volkstümliche} nicht ganz exakte Beschreibung (helft mir mit einer bessern aus --- email!). Das Konzept der \emph{Stecker} (sockets) ist ein Weg, eine Kommunikation zwischen Prozessen herzustellen (IPC; inter process communication). Es befähigt Programme auf demselben Computer, sich zu finden und miteinander zu reden. Aber das ist nicht alles. Stecker funktionieren auch für Programme, die in einem Netzwerk laufen --- überall im Internet, falls du so verdrahtet bist. Um Stecker für eine IPC zu haben, musst du Dienstprogramme und Nutzerprogramme schreiben (server and client program). Der Dienst (server) läuft auf deinem eigenen (local) Computer und ist unter deiner Kontrolle. Die Nutzerprogramme (clients) hingegen können irgendwo im Netzwerk sein, auch auf deinem eigenen Computer als spezieller Fall, und brauchen nicht von dir selbst gemacht worden zu sein. Der Dienst horcht an einem Eingang (port) nach Clienten die in Kontakt kommen wollen. Solche entlegenen (remote) Clienten müssen den Namen deines Computers und die Nummer des Eingangs wissen, an dem dein Dienst wartet. Populäre Beispiele für Dienste auf fast allen Computern sind \texttt{FTP} (port 20), \texttt{TELNET} (port 23), \texttt{FINGER} (port 79), \texttt{TIME-OF-DAY} (port 13) und \texttt{QUOTE} (port 17). Dein eigener Dienst nimmt einfach eine Eingangsnummer, die gerade frei ist, zum Beispiel 3145. Die meisten Stecker--IPCs senden Zeichenketten (strings) zwischen Dienst und Nutzer hin und her und folgen dabei irgendeinem der veröffentlichten Vorschriften (protocol). Für den eigenen Gebrauch hingegen kann man sich einfach selbst was ausdenken, das klappt. Und weil man Forth benutzt, ist das Protokoll der einfachste Teil: Zeichenketten senden (counted strings)\footnote{COUNTED STRINGS sind Zeichenketten, in denen das erste Byte die Längenangabe der Kette enthält. Die Empfangsroutine legt solch eine Zeichenkette im Speicher ab und hinterlegt auf dem Stack die zugehörige Speicheradresse und die Länge der Zeichenkette ( -- adr u ). EVALUATE nimmt dann diese Daten vom Stack, um die Zeichenkette auszuwerten. So kann EVALUATE auf alle möglichen Speicherstellen zugreifen, um dort in Zeichenketten abgelegte Anweisungen auszuführen. EVALUATE ist somit vektorisiert über den Datenstack des Forth. Dieser Mechanismus ist im Forth schon implementiert und braucht daher für eigene Server nicht erst erfunden zu werden.} und \texttt{EVALUATE} benutzen geht ganz nett. Einen Dienst auf deinem Computer zu starten, hat dann die simple Form: \begin{verbatim} 3145 myserver \end{verbatim} Dabei ist 3145 die Eingangsnummer, an der dein Dienst horcht\footnote{Die Nummer ist für das Beispiel willkürlich gewählt worden.} . Und \texttt{myserver} ist ein Forth--Wort (colon definition), das vielleicht eine Endlosschleife enthält. So prüft es, ob ein Client am Eingang 3145 Kontakt sucht, und tauscht dann Zeichenketten mit dem Clienten, wobei ein selber entworfenes Protokoll benutzt werden kann --- Vorschriften dafür gibt es nicht. Die Kommunikation durch diese Stecker geht in beide Richtungen (bidirektional). Die Aufgabe für den Clienten ist genauso simpel. Nehmen wir an, der Client liefe auch auf dem eigenen Computer, dann könnte man so starten: \begin{verbatim} S" //." 3145 myclient \end{verbatim} oder mit \begin{alltt} S" frunobulax" 3145 myclient \hspace{5mm}\mbox{\rm,} \end{alltt} wenn dein Computer \texttt{frunobulax} heißt. Neuerzeits kennt iForth das Wort \emph{HOSTNAME ( -- addr u )}, welches automatisch der Computernamen herausfindet und auf dem Stack ablegt. Jede URL ist ok, solange du eine gültige Port--Nummer angibst und dein Server dahinter aktiv ist. Bis du hierher den Ausführungen gefolgt, kannst du mehr darüber lernen, indem du dir die Dateien holst, die oben genannt worden sind. Denn damit wird iForth um die folgenden Worte erweitert: \begin{small} \begin{description} \item[\tt OPEN-SERVICE] \verb|( c-addr u port# -- socket )|\\ example: \verb|S" frunobulax" 3145 OPEN-SERVICE ( -- 4 )| \item[\tt CREATE-SERVER] \verb|( port# -- lsocket )|\\ example: \verb|3145 CREATE-SERVER ( -- 16 )| \item[\tt LISTEN] \verb|( lsocket /queue -- ) |\\ example: \verb|16 7 LISTEN| \item[\tt ACCEPT-SOCKET] \verb|( lsocket -- socket ) |\\ example: \verb|16 ACCEPT-SOCKET ( -- 3 )| \item[\tt READ-SOCKET] \verb|( socket c-addr maxlen -- c-addr size ) |\\ example: \verb|3 PAD 125 READ-SOCKET ( -- 'pad 125 )| \item[\tt WRITE-SOCKET] \verb|( c-addr size socket -- ) |\\ example: \verb|PAD 128 3 WRITE-SOCKET| \item[\tt CLOSE-SOCKET] \verb|( socket -- ) |\\ example: \verb|3 CLOSE-SOCKET| \item[\tt +CR] \verb|( c-addr1 u1 -- c-addr2 u2 ) |\\ appends a hard CR+LF pair to the text \item[\tt BLOCKING-MODE] \verb|( socket on/off -- ) |\\ put the socket in/out of blocking mode \item[\tt SET-SOCKET-TIMEOUT] \verb|( u -- ) |\\ set the global timeout (in ms) on reading sockets \item[\tt GET-SOCKET-TIMEOUT] \verb|( -- u ) |\\ get the global timeout (in ms) on reading sockets \end{description} \end{small} \section{Die Röhren (Named Pipes)} Noch eine volkstümliche unexakte Beschreibung. (Hast du eine bessere? Email mir!) Eine \emph{benannte Röhre} oder Leitung ist der zweite Weg, Interprozesskommunikation (IPC) zu betreiben. Programme auf dem selben Computer (Linux), oder irgendwo im Netzwerk oder Internet\footnote{Windows ließ dies auch mal zu, aber nur für seine NT Dateisysteme.}, könne sich gegenseitig ausfindig machen und miteinander reden. Der Unterschied gegenüber dem Stecker--Modell ist, dass man keine von diesen ziemlich willkürlich festgelegten Eingangsnummern benutzen muss, dafür aber einen (ebenso willkürlichen :-) Dateinamen. Und ich kenne bisher auch keine öffentlich erreichbaren Röhren die im Internet einen Namen hätten. Wie bei den Steckern so schreibt man auch hier Forth--Programme für beide Seiten, den Dienst und den Clienten, und das IPC arbeitet ebenfalls mit Zeichenketten, die hin und her gehen und die einem Protokoll folgen, das man selbst entwirft. Einen solchen benamten Röhren--Dienst auf deinem Computer zu starten, hat dann die simple Form: \begin{verbatim} S" //./pipe/forthserver" serverspeed ( NT ) S" /dev/forthserver" serverspeed ( Linux ) \end{verbatim} Hier definiert der Name den Pfad, den der Client nehmen muss, um zu deinem Server zu gelangen (auch dieses Beispiel ist erfunden). Beachte den Teil, der da \texttt{/pipe} heißt, das ist für Windows so erforderlich. Das Beispiel--Forthwort \texttt{serverspeed} wiederum enthält auch --- möglicherweise --- eine Endlosschleife. Sie prüft, ob ein Client den Kontakt sucht, und tauscht dann wieder Zeichenketten mit dem Clienten aus, wieder in einem eigens ersonnenen Protokoll. Auch die Kommunikation in den Pipes ist bidirektional. Und auch der Teil für den Clienten ist einfach. Falls der Client auch auf deinem Computer läuft, starte ihn z.~B.\ so: \begin{verbatim} S" //./pipe/forthserver" clientspeed \end{verbatim} oder \begin{verbatim} S" //frunobulax/pipe/forthserver" clientspeed \end{verbatim} oder \begin{alltt} S" /dev/forthserver" clientspeed \hspace*{5mm}\mbox{\rm ,} \end{alltt} falls dein Computer wieder \texttt{frunobulax} heißt. (Linux unterstützt allerdings keine Pipes die über ein Netzwerk hereinkommen.) Auch nun kannst du mehr dazu erkunden, indem du die Dateien holst, die sich mit den \emph{named pipes} befassen. Und iForth wird dadurch um folgende Worte erweitert: \begin{small} \begin{description} \item[\tt OPEN-NAMED-PIPE] \verb|( c-addr u timeout -- handle ) |\\ example: \\ \begin{footnotesize} \verb|S" //frunobulax/pipe/clientspeed" 5000 OPEN-NAMED-PIPE| % ( -- 3 ) \end{footnotesize} \item[\tt CREATE\&ACCEPT] \verb|( c-addr u -- handle ) |\\ example: \\ \begin{footnotesize} \verb|S" //frunobulax/pipe/serverspeed" CREATE&ACCEPT| % ( -- 3 ) \end{footnotesize} \item[\tt READ-NAMED-PIPEX] \verb|( c-addr maxlen handle -- c-addr size ) |\\ example: \verb|PAD 125 3 READ-NAMED-PIPEX ( -- 'pad 125 )| \item[\tt WRITE-NAMED-PIPE] \verb|( c-addr size handle -- ) |\\ example: \verb|PAD 128 3 WRITE-NAMED-PIPE| \item[\tt CLOSE-NAMED-PIPE] \verb|( handle -- ) |\\ example: \verb|3 CLOSE-NAMED-PIPE| \end{description} \end{small} Vielleicht kannst du diesen Satz an Forthworten etwas leichter verstehen als das \emph{socket wordset}, aber die Stecker sind universeller einsetzbar, sie können jede URL benutzen. Systeme, auf denen iForth sockets und pipes unterstützt Windows NT, XP, Vista, 7\\ Beides, das socket und das pipe wordset, funktionieren mit Windows in jeder Version. Eigentlich bräuchte das iForth gar keine speziellen read/write/close Wörter für Linux, denn die Schnittstellen (handles und sockets) sind kompatibel mit den Worten, die auch für das Dateisystem da sind --- \texttt{READ-FILE} \texttt{WRITE-FILE} und \texttt{CLOSE-FILE}. Aber wegen des Microsoftismus funktioniert es so nicht unter Windows. Deshalb hat das iForth andere Namen für die read/write/close Aktionen genommen, damit die Unterschiede dahinter einfach auseinandergehalten werden können. Das named pipe wordset ist auf einzelne Clienten, die mit einem einzelnen Server verbunden sind, beschränkt. (Eigentlich sollte Windows multiple Clienten unterstüzten, aber es war nicht herauszufinden, wie es das macht.) Linux 2.0.x und Mac OS X (Intel)\\ Beides, das socket und das pipe wordset, funktionieren mit Linux jeder Version. Beim Linux sind die pipes nicht so nützlich, da sie dort im Netzwerk nicht funktionieren. Aber man kann hier mehrere Klienten an einen Server anbinden. Die pipes wurden auf sockets und dem etwas modifizierten Code von W.~Richard Stevens \emph{Advanced Programming in the UNIX Environment} aufgebaut\footnote{Advanced Programming in the UNIX Environment. (Addison--Wesley Professional Computing Series) von W. R. Stevens und Stephen A. Rago von Addison--Wesley Longman, Amsterdam (Gebundene Ausgabe --- 7. Juli 2005); bei Amazon.de erhältlich.}. \section{Die Leitungsfähigkeit} Es gab einen erkennbaren Unterschied in der Performance zwischen sockets und named pipes. Die Tests wurden folgendermaßen durchgeführt (Stand 09.06.2009): Benchmark 1: 20 Mbytes kopiert zwischen zwei iForths auf Maschine 1; \\ Benchmark 2: 20 Mbytes kopiert zwischen einem iForth von Maschine 2 an ein iForth auf Maschine 1. Systeme für den sock--Test:\\ Windows XP Pro, einmal (1) auf einem Intel PIV 3GHz/1GB und (2) auf Intel Core 2 Duo 2.66 GHz/2GB. Die PCs waren verbunden über motherboard Realtek network adapters (100 Mbit/s). Systeme für den npipe--Test:\\ Windows NT 4.0, einmal (1) auf Intel Pentium 166MHz/48MB und (2) auf Intel Pentium 200MHz/48MB. Die PCs waren im Netzwerk verbunden über preiswerte NE2000 clones (10 Mbit/s). \begin{center} \begin{tabular}{l|r|r} benchmark & process A read & process B write\\ \hline sock bm 1 & 72 MB/sec & 732 MB/sec \\ sock bm 2 & 11.5 MB/sec & 2.857 GB/sec \\ npipe bm 1 & 15 MB/sec & 15 MB/sec \\ npipe bm 2 & 715 KB/sec & 715 KB/sec \\ \end{tabular} \end{center} Die Tests wurden auch unter Linux durchgeführt und verliefen noch etwas besser dort. Wie man sieht, arbeiten die sockets schnell bei Prozessen auf verschiedenen Maschinen. Hingegen schnitten die pipes bei Prozessen, die auf demselben Rechner lagen, besser ab als wenn sie im Netzwerk verwendet wurden. Unter Linux gibt es fast keinen Unterschied zwischen sockets und pipes. Ich rate dazu, sockets zu verwenden. Sie sind portabel, schnell, und arbeiten im Netzwerk ebenso wie innerhalb des Betriebssystems\footnote{Unglücklicherweise funktionieren pipes mit Namen nicht unter Windows XP Pro. Die Windows SDK informiert darüber, dass dafür mindestens der Windows 2000 Server benötigt wird. Daher sind die pipes in ihrer Nützlichkeit doch erheblich eingeschränkt, und Marcel Hendrix überlegt derzeit (mail 9.6.2009) ob in zukünftigen Versionen diese im iForth eingebaute Unterstützung nicht ganz entfallen sollte.}. \section{Der Quellcode} In seinem WEB--Beitrag gibt Marcel Hendrix links an, unter denen man, je nach Betriebssystem, die passenden Quellen findet, um sockets und pipes in iForth realisieren zu können. Dazu wird jeweils ein Stück C--Code benötigt (iForth Server C--part) um die basale Anbindung von iForth an das Betriebssystem aufzubauen. Und dann ein Stück Forthcode, um diese C Funktionen mit dem Forth Socket Wordset zu verbinden. Der folgende screenshot zeigt, was geboten wird. \end{multicols} \begin{center} \includegraphics[height=4.5cm]{2009-03/Crystal-Clear-filesystem-socket}\\ Crystal Clear filesystem socket (Quelle: wikimedia.org) \end{center} \newpage \section{Links} Marcel Hendrix\\ \url{http://home.iae.nl/users/mhx/pipes%26socks.html} \url{http://home.iae.nl/users/mhx/i4faq.html} \section{Glossar} \begin{description} \item[Socket] Ein Socket (engl.; Sockel oder Steckverbindung) ist eine bidirektionale Software-Schnittstelle zur Interprozess- (IPC) oder Netzwerk-Kommunikation. Sockets sind vollduplexfähige Alternativen zu Pipes oder Shared Memory. Sockets bilden eine plattformunabhängige, standardisierte Schnittstelle (API) zwischen der Netzwerkprotokoll-Implementierung des Betriebssystems und der eigentlichen Anwendungssoftware. \item[API] Eine Programmierschnittstelle, die von einem Softwaresystem anderen Programmen zur Anbindung an das System zur Verfügung gestellt wird. Oft wird dafür die Abkürzung API (für engl. application programming interface, deutsch: „Schnittstelle zur Anwendungsprogrammierung“) verwendet. Im Gegensatz zu einer Binärschnittstelle (ABI) definiert eine API die Verwendung der Schnittstellen auf Quelltextebene. Programmierschnittstellen werden in folgende Klassen einteilen: funktionsorientiert (z. B. Dynamic Link Library) dateiorientiert (z. B. Gerätedateien unter UNIX) objektorientiert (z. B. ActiveX-DLLs) protokollorientiert (z. B. FTP) \item[Pipe] Die Pipe (englisch für Rohr, Röhre) bezeichnet einen gepufferten uni- oder bidirektionalen Datenstrom zwischen zwei Prozessen nach dem „First In – First Out“-Prinzip. Das heißt vereinfacht, dass die Ausgabe eines Prozesses (ein Programm in Ausführung) als Eingabe für einen weiteren verwendet wird. \item[URL] Als Uniform Resource Locator (URL, dt. „einheitlicher Quellenanzeiger“) bezeichnet man eine Unterart von Uniform Resource Identifiern (URIs). URLs identifizieren und lokalisieren eine Ressource über das verwendete Netzwerkprotokoll (beispielsweise HTTP oder FTP) und den Ort (engl. location) der Ressource in Computernetzwerken. Da URLs die erste und häufigste Art von URIs darstellen, werden die Begriffe häufig synonym verwendet. In der Umgangssprache wird URL häufig als Synonym für Internetadresse verwendet. \end{description} \hspace*{\fill}(Quelle: Wikipedia) \vfill \begin{center} \includegraphics[height=10cm]{2009-03/1998-11-16-Pipes}\\ 1998-11-16-Pipes (Quelle: wikimedia.org) \end{center} \vfill