šcOberon10.Scn.Fntš’’’š¹õą„ParcElemsAlloc Ęź ¼żOberon16b.Scn.Fnt5Oberon12.Scn.Fnt   ī’’’ ŒIą„ĄĘ'€ńž°š Šķ@ż’’’€ø?'LineElemsAlloc@Oberon14b.Scn.FntŪż’’’€ø?'ķ’’’ £Zą„ Ęź°š Šķą°Courier10.Scn.Fntƒ mķ’’’ £Zą„ Ęź°š Šķą°ėš’’’ £Zą„ Ęź°š Šķ„lį’’’ £Zą„€×ą ļ‰ ¼ż€Ļą¢ē „†6š’’’ £Zą„ Ęź°š Šķģ’’’ £Zą„ĄƒjĄįąäĖ°š Šķ¶ģ’’’ £Zą„ą·cĄ9ąµ±°š Šķ,š’’’ £Zą„ Ęź°š ŠķŠOberon12i.Scn.Fnt ģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š Šķlę’’’ £Zą„ ć€ćÖ°š Šķ šā€†ūć’’’ £Zą„ ć€ćÖ ¼ż€Æą¢ēĄø’/ģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š ŠķŻē’’’ £Zą„ ć€ćÖ°š Šķą° ˜©ē’’’ £Zą„ ć€ćÖ ¼żą•ąÆ¶)ē’’’ £Zą„ ć€ćÖ ¼ż€”#€®Įæē’’’ £Zą„ ć€ćÖ ¼żą•Ą–“ ģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š Šķčģ’’’ £Zą„ąu ć€ćÖ°š Šķī’’’ £Zą„ ć€ćÖ°š Šķī’’’ £Zą„€å įį°š Šķš’’’ £Zą„ Ęź ¼żOberon12b.Scn.Fnt/ī’’’ £Zą„€å įį°š Šķ}ģ’’’ £Zą„ąu ć€ćÖ°š Šķ-ī’’’š¹õą„ ć€ćÖ°š Šķ:!  ±ģ’’’ £Zą„ąu ć€ćÖ°š Šķ.ī’’’ £Zą„ ć€ćÖ°š Šķc'@ģ’’’ £Zą„ąu ć€ćÖ°š Šķī’’’ £Zą„ ć€ćÖ°š Šķģ’’’ £Zą„ąu ć€ćÖ°š Šķ .ī’’’ £Zą„ ć€ćÖ°š Šķµģ’’’ £Zą„ąu ć€ćÖ°š Šķ.ī’’’ £Zą„ ć€ćÖ°š ŠķŸ ±ģ’’’ £Zą„ąu ć€ćÖ°š Šķ0ī’’’ £Zą„ ć€ćÖ°š Šķr O R ģ’’’ £Zą„ąu ć€ćÖ°š Šķ ;ī’’’ £Zą„ ć€ćÖ°š Šķčģ’’’ £Zą„ąu ć€ćÖ°š Šķ #ī’’’ £Zą„ ć€ćÖ°š Šķģ’’’ £Zą„ąu ć€ćÖ°š Šķ *ī’’’š¹õą„ ć€ćÖ°š Šķ©ģ’’’ £Zą„ąu ć€ćÖ°š Šķī’’’ £Zą„ ć€ćÖ°š Šķbī’’’ £Zą„€å įį°š Šķ,—ģ’’’ £Zą„ąu ć€ćÖ°š ŠķH   WMIģ’’’ £Zą„ąu ć€ćÖ°š Šķ7ī’’’ £Zą„ ć€ćÖ°š ŠķEģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š Šķs00ūģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š Šķ5 n Żģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š Šķģ!ģ’’’ £Zą„ąu ć€ćÖ°š Šķ ī’’’ £Zą„ ć€ćÖ°š ŠķoÜ ī’’’ £Zą„€å įį°š Šķ“-ģ’’’ £Zą„ąu ć€ćÖ°š Šķ    ī’’’ £Zą„ ć€ćÖ°š ŠķXģ’’’ £Zą„ąu ć€ćÖ°š Šķī’’’ £Zą„ ć€ćÖ°š ŠķŻ‡ģ’’’ £Zą„ąu ć€ćÖ°š Šķī’’’ £Zą„ ć€ćÖ°š Šķš’’’ £Zą„ Ęź°š Šķw /ģ’’’ £Zą„ąu ć€ćÖ°š ŠķāYī’’’ £Zą„ĄįąäĖ°š Šķīģ’’’ £Zą„ąu ć€ćÖ°š ŠķĻOberon14i.Scn.Fnt%ļģ’’’ £Zą„ źg€Ž4 čµ°š ŠķÅī’’’ £Zą„ Č€žĶ°š Šķ‚ģ’’’ £Zą„ąu ć€ćÖ°š Šķ0 ÓÓA ‚ģ’’’ £Zą„€ģ\€ł+ Ķ¾°š Šķ~ģ’’’ £Zą„ąuąÄ2Ąø°š Šķc8 ‰UĮģ’’’ £Zą„Ąžaąß)ĄęĄ°š Šķ„ģ’’’ £Zą„ąu€Ž4 čµ°š Šķoī’’’ £Zą„€å įį°š Šķ#ģ’’’ £Zą„ąu ć€ćÖ°š Šķ  D "" " ) D    & ŽB ,³ Š Ćjtƒ,cī’’’ £Zą„€å įį°š Šķ9ģ’’’ £Zą„ąu ć€ćÖ°š Šķ3<@Bš’’’ £Zą„ Ęź°š Šķ—&ąš’’’ £Zą„ Ęź ¼żaš’’’ £Zą„ Ęź°š Šķ™‹ć Ż„Portierung von Netsystem auf Windows_ und UNIX_OberonTM. Daniel Busser  Inhalt 1 Die Aufgabe 2 Einleitung 3 Berkeley Sockets 4 Die Implementierung 4.1 NetBase 4.2 NetSystem 5 Schwierigkeiten und L„sungen 6 Anleitung zur weiteren Portierung 7 Erweiterungen 8 Literaturangaben  1 Die Aufgabe TCP/IP ist eines der am weitest verbreiteten Protokolle f…r Ethernet Netzwerke. Das Protokoll wurde schon erfolgreich auf DOSOberon System 3 und MacOberon portiert. Zur Zeit fehlen noch Implementationen f…r UNIX und Windows Oberon. Um diese L…cke zu schliessen, soll eine auf Sockets basierende Version von NetSystem erstellt werden, die auf Windows und allen UNIX Plattformen lƒuft. 2 Einleitung Der Zugriff auf das Internet wird in Oberon Systemen …blicherweise vom Modul Netsystem gewƒhrleistet, das dem Benutzer ein einfaches Interface anbietet. Das Hauptziel war es eine so weit wie m„glich maschinenunabhƒngige Version von Netsystem zu erstellen, um eine h„chst einfache Portabilitƒt zu erreichen. Auf der Ceres_2 muss die Ethernet Karte der Maschine direkt programmiert werden. Von Portabilitƒt kann in diesem Fall nat…rlich keine Rede sein. Auf den meisten kommerziellen Maschinen ist heute jedoch eine Programmierschnittstelle (API: application program interface) zum TCP/IP Kommunikationsprotokoll verf…gbar, welche von BSD_UNIX stammt und den Namen Sockets trƒgt. Auf Sockets basierend kann Netsystem viel kompakter und portabler implementiert werden. Der erste Schritt in Richtung Portabilitƒt wƒre also getan. Jedoch k„nnen auch somit nicht alle maschinenspezifischen Details umgangen werden. Der zweite Schritt bestand also darin, Netsystem in zwei Module zu unterteilen: _ Netsystem selbst, maschinenunabhƒngig und als einziges vom Benutzer importiert. _ NetBase, maschinenabhƒngig und von Netsystem importiert. NetBase sollte der Portabilitƒt wegen sich auf das Wesentliche beschrƒnken. Seine Hauptaufgabe ist es Prozeduren zur Verf…gung zu stellen, welche ihrerseits Prozeduren einer Sockets_Bibliothek des zugrundeliegenden Systems aufrufen. Die Socketsschnittstelle und die damit implementierten Protokolle TCP/IP und UDP/IP waren das erste, womit ich mich befasst habe. 3 Berkeley Sockets Auf IP (Internet Protocol, verbindungslos, unzuverlƒssig) beruhen mehrere Protokolle, von denen die zwei wichtigsten TCP (Transmission Control Protocol, verbindungsorientiert, zuverlƒssig, voll_duplex, Byte_Strom) und UDP (User Datagram Protocol, verbindungslos, unzuverlƒssig) sind. Ein genauerer Vergleich dieser drei Protokolle liefert Tabelle 1. (aus [Ste90])  Feature IP UDP TCP ________________________________________________________________________________ connection_oriented no no yes message boundaries yes yes no data checksum no opt. yes positive acknowledge no no yes timeout no no yes duplicate detection no no yes sequencing no no yes flow control no no yes  Aus dieser Tabelle ist leicht ersichtlich, dass UDP ausser einer optionalen Checksumme zur Verifizierung der Korrektheit der Daten nichts Neues auf IP aufsetzt, wobei TCP eine v„llig andere Kommunikationsart darauf aufbaut. Der einzige gemeinsame Zusatz sind Port Nummern, die es erm„glichen, auf verschiedenen durch diese Nummern identifizierten virtuellen Kanƒlen gleichzeitig zu kommunizieren. Diese Unterschiede haben nat…rlich einen Einfluss auf die Folge der verschiedenen Socketsbefehle, die f…r eine gewisse Kommunikationsart gebraucht werden. Zusƒtzlich spielt es eine Rolle, ob ein Prozess als Klient oder als Server agieren soll. Auf jeden Fall muss jedoch zuallererst auf jeder an der Kommunikation beteiligten Maschine ein Socket erstellt werden. Dies geschieht mit dem Befehl socket(). Ein Socket ist ein Kommunikationsendpunkt, von dem aus und zu dem Daten geschickt werden k„nnen. Eine Kommunikation ist jedoch erst m„glich, wenn alle Elemente des 5_Tupels festgelegt sind. Je nach TCP_/UDP_ und _Klient/_Server Paar wird daf…r auf verschiedene Weise vorgegangen: _ UDP: Es wird kein Unterschied zwischen Klient und Server gemacht. Dem Socket muss eine eindeutige Adresse zugewiesen werden. Diese besteht aus der Internet_Adresse der Maschine und einer Port Nummer. Diese Adresse wird mit dem bind() Befehl an den Socket gebunden. Da verbindungslos kommuniziert wird und bei jedem neu…bertragenen Packet die vollstƒndige Adresse des Partners mitgegeben werden muss, reicht es aus, wenn jeder socket eindeutig identfizierbar ist, um mit UDP zwischen ihnen zu kommunizieren. _ TCP: Bei verbindungsorientierter Kommunikation muss, wie der Name es sagt, zuerst eine Verbindung hergestellt werden. Es wird grundsƒtzlich zwischen Klient und Server unterschieden _ Server: Der Server muss, wie auch bei UDP, dem Socket eine eindeutige Adresse zuweisen. Dies geschieht auch hier mit bind(). Danach muss der Server den Socket in einen "lauschenden" Zustand setzen, um abzuh„ren, ob jemand sich mit ihm in Verbingung setzen will. Dazu steht der listen() Befehl zur Verf…gung. Wenn er in diesem Zustand ist, kann der Server den accept() Befehl ausf…hren, der wartet bis sich dann tatsƒchlich ein Klient meldet. Accept() erstellt dann einen weiteren Socket …ber den die Kommunikation mit dem Klienten stattfinden wird und gibt den Hauptsocket wieder zum Empfang weiterer Verbindungsaufforderungen frei. _ Klient: Um mit einem TCP_Server eine Verbindung aufzubauen, braucht der Klient nur den Befehl connect() auszuf…hren. Connect() bindet selber die lokale Adresse an den Socket.  Von nun an kann mit send() und recv() (TCP) bzw. sendto() und recvfrom() (UDP) kommuniziert werden. Nachdem die Kommunikation beendet ist, k„nnen beide Parteien mit close den allozierten Socket wieder freigeben. Dies scheint nun alles sehr einfach und man k„nnte sich fragen, warum man das Sockets Interface nicht direkt in Oberon …bernommen hat. Dagegen wƒre erstens einzuwenden, dass sich das Sockets Interface nicht nur auf die oben genannten Befehle beschrƒnkt. Es beinhaltet noch eine Menge anderer _ zum Teil redundanter _ Befehle zum Abfragen des Zustandes eines jeden Sockets, zum Setzen von Dutzenden Optionen, usw. Dies kann sicher n…tzlich sein, erschwert aber meistens auf unn„tige Weise die Programmierung. Ausserdem ben„tigt jeder Befehl eine grosse Anzahl Parameter, was wiederum vom Benutzer eine ziemlich detaillierte Kenntnis verlangt. Wenn man noch hinzuf…gt, dass die oben beschriebene Folge von Befehlen absolut nicht vorgeschrieben ist, und es zum Beispiel nicht verboten ist, connect(), send() und recv() auf UDP Sockets, sendto() und recvfrom() auf TCP Sockets oder bind() auf TCP_Klienten zu verwenden und wenn sich dar…berhinaus der Benutzer mit Begriffen wie "out_of_band data" oder einem "linger" vertraut machen sollte, liegt es nahe ein neues Interface zu definieren, das sich auf das Wesentliche beschrƒnkt, und mit dem dennoch jeder m„gliche Dienst programmiert werden kann. 4 Die Implementierung 4.1 NetBase Da nicht nur das Interface von NetSystem auf allen Maschinen gleich sein soll, sondern das ganze Modul, muss es sich auf eine eindeutige Schnittstelle st…tzen k„nnen, die auf allen Plattformen gleich ist. Man k„nnte denken, dass die Socketsschnittstelle diesen Wunsch erf…llt; dem ist aber in Wirklichkeit nicht so. Ein solches uniformes Interface muss also zuerst von einem Basismodul erstellt werden. Daf…r werden in NetBase die Sockets_Prozeduren an Prozedur_Variablen gebunden. Die Prozeduren die von NetBase exportiert werden, rufen …ber diese Prozedur_Variablen die eigentliche Sockets_Prozedur auf und beseitigen gleichzeitig die Unterschiede zwischen den verschiedenen Plattformen. Solche Unterschiede k„nnen systemabhƒngig sein, d.h. aus oft unersichtlichen Gr…nden wurden gewisse Werte anders gewƒhlt als im original BSD_UNIX System. Jedoch gibt es auch maschinenabhƒngige Unterschiede wie z.B. die Byte_Ordnung. Aus der Komplexitƒt der Socketsschnittstelle ist leicht ersichtlich, dass Inkompatibilitƒten in allen Gebieten existieren: _ Konstanten: Es gibt relativ wenige Unstimmigkeiten bei den Konstantendeklarationen. Jedoch gibt es auch hier Ausnahmen. Beispiel: UDP TCP  SPARC_Solaris 1 2 HP_Unix und Windows 2 1  _ Datentypen: Ein unumgƒngliches, maschinenabhƒngiges Problem ist der Unterschied zwischen "big_endian" und "little_endian" Byte_Ordnungen. Die Byte_Ordnungen spielt bei Datentypen die gr„sser als ein Byte sind eine Rolle. Rechner von HP arbeiten z.B. mit einer big_endian Ordnung, wobei SPARC und Intel 80x86 Prozessoren mit little_endian Ordnungen rechnen. Auf dem Internet selber werden die Daten in big_endian Ordnung …bertragen. Deshalb gibt es in der Sockets_Bibliotheken auch spezielle Prozedurfunktionen zur Konversion von Byte_Ordnungen. Es handelt sich dabei um htonl(), htons(), ntohl() und ntohs(). Dies ist aber nicht der einzige Unterschiede. Auch hier gibt es "vermeidbare" Schnittstellenunterschiede von einem System zum anderen. Beispiel:  _ HP_UNIX: _ Windows:  hostentdesc= RECORD hostentdesc= RECORD  hname: LONGINT; hname: LONGINT; haliases: LONGINT; haliases: LONGINT; haddrtype: LONGINT; haddrtype: SHORTINT; hlength: LONGINT; hlength: SHORTINT; haddrlist: LONGINT haddrlist: LONGINT  END; END;  _ Prozeduren: Das Problem der Byte_Ordnungen begegnet uns auch hier wieder. Auf einer HP_Maschine braucht und gibt es nƒmlich gar keine Konversionsprozeduren, da die Maschine die gleiche Byte_Ordnung wie das Netz verwendet. Die Schnittstelle zu NetSystem muss jedoch auf allen Plattformen gleich sein. Also m…ssen in NetBase f…r HP_UNIX die Prozedurfunktionen htonl(), usw. nicht aus der Sockets_Bibliothek gelesen werden sondern als Identitƒtsfunktionen programmiert und exportiert werden. Auf der Windows_Plattform gibt es zwei zusƒtzliche Prozeduren WSAStartup() und WSACleanup(), die dazu dienen das ganze darunterliegende System zu initialisieren bzw. aufzurƒumen. Dabei muss vor jeder Arbeitssession mindestens ein Aufruf von WSAStartup() erfolgen und WSACleanup muss genauso oft aufgerufen werden wie WSAStartup aufgerufen wurde. Die Prozedur gethostname() scheint auf SPARC_Solaris nicht zu funktionieren und es muss …ber die Prozedur sysinfo() auf den Namen der lokalen Maschine zugegriffen werden. Auf der Windows Oberflƒche muss der Prozedur gethostbyname() der Name der gew…nschten Maschine in Grossbuchstaben …bergeben werden, was gerade auf einem System das im Normalfall keinen Unterschied zwischen Gross_ und Kleinbuchstaben macht …berraschend ist. _ Prozedur_Bibliotheken: Die Namen der Bibliotheken in denen sich die Sockets_Prozeduren befinden sind nat…rlich von System zu System verschieden. Auf Windows heisst die entsprechende Datei "WSOCK32.DLL", auf HP muss in "libc.sl" gesucht werden. Jedoch brauchen sich die Sockets_Prozeduren nicht alle in der gleichen Bibliothek zu befinden. Auf SPARC_Solaris befinden sich die Prozeduren sch„n geordnet in verschiedenen Bibliotheken, da manche auch noch zu anderen Schnittstellen geh„ren. Die Hauptdatei heisst "libsocket.so", aber Prozeduren zur Umwandlung von Maschinennamen in Internet_Adressen befinden in der Datei "libnsl.so" und sysinfo() steht in "libc.so".  Ausserdem erlaubt es das zwischengeschaltete Modul NetBase, Aufrufe des Pseudo_Moduls SYSTEM, d.h. die Unsch„nheiten der systemnahen Programmierung, von NetSystem fernzuhalten. Die Leserlichkeit von NetSystem wird somit um einiges erh„ht, besonders auch weil externen Prozeduren die …ber Prozedur_Variablen angesprochen werden nur Parameter des Typs LONGINT …bergeben werden k„nnen. SYSTEM wird also auch ben„tigt um Adressen von Variablen zu …bergeben, die die Sockets_Prozedur als "by reference"_Parameter oder auch als Pointer verlangt.  Genauere Beschreibung der einzelnen Prozeduren.  NetBase exportiert eine globale bool'sche Variable done, die angibt ob die letzte Prozedur erfolgreich terminieren konnte. Diese Variable wird von allen Prozeduren von NetBase gesetzt. Diese Methode wƒre in einem Multi_Prozess System zwar schlecht, da man nicht eindeutig bestimmen k„nnte, welcher von mehreren gleichzeitigen Prozessen als letzter die globale Variable gesetzt hat, aber in einem Single_Prozess Multi_Tasking System wie Oberon gibt es dieses Problem nicht, und eine globale Variable wie sie auch im Modul Input benutzt wird, trƒgt viel zur Einfachheit der Anwendung bei. In einem Multi_Prozess System wƒre man gezwungen jeder Prozedur einen Parameter mitzugeben, der angibt, ob die Prozedur Erfolg hatte. Zur Parameter…bergabe ist zu bemerken, dass externen Prozeduren, die …ber Prozedur_Variablen angesprochen werden nur Parameter des Typs LONGINT …bergeben werden k„nnen.  Accept*(s: LONGINT; VAR newsocket(*out*): LONGINT); Ruft accept(s, adrPtr, adrlenPtr: LONGINT) mit dem Socket s auf und gibt einen neuen Socket newsocket zur…ck. Die Parameter adrPtr und adrlenPtr dienen dazu, die Adresse des anrufenden Sockets zu erfahren. Dies ist f…r NetSystem nicht erforderlich, und es wird deshalb 0 …bergeben. Der neue Socket wird von accept() erzeugt, wenn eine Verbindungsaufforderung f…r den Haupt_TCP_Socket besteht. Nachdem der neue Socket erstellt wurde, und damit eine Verbindung zum anrufenden Socket, wird der Haupt_Socket von accept() f…r weitere Verbindungsaufforderungen wieder freigegeben. Bind* (s: LONGINT; VAR sockaddr(*in*): Sockaddrin); Mit bind(s, adrPtr, adrlen: LONGINT) wird die Adresse die in sockaddr …bergeben wird an den Socket s gebunden, das heisst, dass der Socket s nach dieser Operation einen weltweit eindeutigen Namen trƒgt. Close* (s: LONGINT); Nach der Operation close(s: LONGINT) kann der Socket nicht mehr zur Kommunikation benutzt werden. Um den Verlust der Daten die zum close() Zeitpunkt noch nicht gesendet waren zu vermeiden, muss auf dem geschlossenen Socket die Linger_Option gesetzt worden sein. Connect* (s: LONGINT; VAR sockaddr(*in*): Sockaddrin); Die Kommunikationsaufforderung wird auf der Seite des Klienten mit connect(s, adrPtr, adrlen: LONGINT) weggeschickt. Connect() bindet auch gleichzeitig eine Adresse an den Socket. GetHostByAdr* (adr: IPAdr; VAR name(*out*): ARRAY OF CHAR); Wenn man eine 32_Bit IP_Adresse kennt, kann man diese GetHostByAdr() brauchen, um den menschlichen Namen der Maschine herauszufinden. Dies geschieht …ber eine host_entry, die von der Prozedur gethostbyaddr(adrPtr, len, type: LONGINT) zur…ckgeliefert wird. Gethostbyaddr() erhƒlt diese Information von einem sogennanten DNS (Domain Name Server). GetHostByName* (name: ARRAY OF CHAR; VAR addr(*out*): IPAdr); Der Name (in menschlicher Form) einer Maschine wird in eine eindeutige IP_Adresse umgewandelt. Dazu wird in einer host_entry nachgeschaut, die von gethostbyname(namePtr: LONGINT) zur…ckgegeben wird. Die host_entry stammt von einem DNS(Domain Name Server), bei dem gethostbyname() nachfragt. Die host_entry enthƒlt einen Zeiger auf eine Liste von Zeigern, die alle auf eine IP_Adresse verweisen. Der Grund des doppelten Verweises ist der, dass es mehrere IP_Adressen f…r eine einzige Maschine geben kann ("multi_homed hosts"). GetHostByName() gibt nur die erste dieser Adressen zur…ck. GetHostByIP* (VAR IPadr(*in*): ARRAY OF CHAR; VAR addr(*out*): IPAdr); Eine Maschine kann auch direkt …ber ihre IP_Adresse angesprochen werden. Die Adresse wird dann meistens als Zeichenkette …bergeben. Diese Form ist auch als "dotted decimal notation" bekannt. GetHostByIP() transformiert diese Zeichenkette von Zahlen einfach in eine brauchbare IP_Adresse, das heisst eine 32_Bit Zahl. Diese Umwandlung k„nnte man zwar auch "von Hand" programmieren, aber die Socketsschnittstelle stellt daf…r die Prozedur inet_addr(cpPtr: LONGINT) zur Verf…gung. Aus zuk…nftigen Kompatibiltƒtsgr…nden, insbesondere die neuen 128_Bit Internet_Adressen, habe ich mich f…r die Sockets_Prozedur entschieden, wobei es sicherlich viel gr„ssere Probleme und €nderungen geben wird, wenn endlich die neuen IP_Adressen eingef…hrt werden. GetHostName* (VAR name(*out*): ARRAY OF CHAR); Diese Prozedur liefert einfach den menschlichen Namen der lokalen Maschine. Auf SPARC muss der Name der Maschine mit sysinfo(cmd, bufPtr, count: LONGINT) abgefragt werden, wƒhrend auf HP_UNIX und Windows das klassische gethostname(namePtr, namelen: LONGINT) funktioniert. GetPeerName* (s: LONGINT; VAR adr(*out*): Sockaddrin); GetPeerName() macht nur bei verbindungsorientierter Kommunikation auf der Serverseite Sinn, da die Prozedur getpeername(s, adr, adrlenPtr: LONGINT) die Adresse des verbundenen Partners liefert. Ein verbindungsorientierter Klient muss den Namen seines Servers nƒmlich schon vor dem Aufbau der Kommunikation kennen, und bei verbindungsloser Kommunikation wird der Name des Adressaten und des Absenders jedem Paket mitgegeben. Listen* (s: LONGINT); Ein Socket, der auf der Serverseite einer TCP_Verbindung ge„ffnet wurde, muss in einen "lauschenden" Zustand versetzt werden, um ankommende Kommunikationsaufforderungen abzufangen. Die Anzahl Verbindungsaufforderungen, die gleichzeitig von einem TCP_Server_Socket verarbeitet werden k„nnen, wird mit backlog angegeben. Ein typischer Wert ist hierf…r 5. Zum wirklichen Austausch der Daten k„nnen die folgenden Prozeduren verwendet werden. Verbindungslos werden verstƒndlicherweise die Prozeduren gebraucht, die einen Absender oder einen Adressaten verlangen. Bei verbindungsorientierter Kommunikation kann man sich diesen Aufwand sparen und sozusagen vom Mehraufwand der Verbindungserstellung leben. Die Prozeduren terminieren nur dann erfolgreich wenn die gesamtheit der gew…nschten Daten …bertragen wurde. Jedoch steht auch bei nicht_erfolgreicher ‚bertragung die Lƒnge der tatsƒchlich …bertragenen Daten in len. Es ist zu bemerken, dass auch beim Empfang die gew…nschte Lƒnge angegeben werden muss, und nicht einfach gelesen wird, bis es keine Daten mehr gibt.  Recv* (s: LONGINT; VAR buf(*out*): ARRAY OF SYSTEM.BYTE; pos: LONGINT; VAR len(*inout*): LONGINT); RecvFrom* (s: LONGINT; from: Sockaddrin; VAR buf(*out*): Data; pos: LONGINT; VAR len: LONGINT); Send* (s: LONGINT; VAR buf(*in*): Data; pos: LONGINT; VAR len(*inout*): LONGINT); SendTo* (s: LONGINT; to: Sockaddrin; VAR buf(*in*): Data; pos, len: LONGINT); Socket* (VAR s(*out*): LONGINT; af, type, protocol: LONGINT); Diese Prozedur liefert einen Wert zur…ck, den sie mit dem socket(af, type, protocol: LONGINT) Aufruf erstellt hat. Um einen Socket zu kreieren, muss angegeben werden, in welcher domain (Unix_domain, Internet_domain, usw) der Socket ben…tzt wird, welche Art der Verbindung es sein soll, und welches Protokoll gebraucht wird. Available* (s: LONGINT): LONGINT; Mit ioctl(s, cmd, argPtr: LONGINT) wird hier abgefragt wieviel Bytes lesbar sind, ohne zu blockieren. Dazu muss in cmd das Socketkommando FIONREAD mitgegeben werden. ArgPtr zeigt dann auf die Anzahl lesbarer Bytes. Wenn done TRUE angibt, dann entspricht der R…ckgabewert der genauen Anzahl Bytes, die zur Verf…gung stehen. Dieser kann nat…rlich auch Null sein. Bei FALSE ist der R…ckgabewert -1; jedoch hat dieser Wert keine Bedeutung, da ioctl() einen Fehler gemeldet hat. Requested* (s: LONGINT): BOOLEAN; Mit dieser Prozedur wird getestet, ob f…r den Socket s eine Kommunikationsaufforderung von einem anderen Socket ansteht. Zu diesem Zweck kann die Prozedur select(nfds, readfdsPtr, writefdsPtr, exceptfdsPtr, timeoutPtr: LONGINT) dienen. Man muss ihr einen kodierten Satz von auf "Lesbarkeit" zu …berpr…fende Sockets in readfdsPtr …bergeben. Select() liefert als R…ckgabewert die Anzahl der Sockets, die tatsƒchlich in Frage kommen, und in readfdsPtr sind die entsprechenden Sockets dann auch gesetzt. Damit select() sofort zur…ckkommt, und nicht etwa wartet, bis mindestens einer der Sockets die gestellten Bedingungen erf…llt, muss der Timeout auf 0,0 gesetzt werden. Connected* (s: LONGINT): BOOLEAN; Um zu testen, ob eine Verbindung noch existiert gibt es keine spezielle Sockets Prozedur. Man kann jedoch testen, ob der Socket auf der Partnerseite geschlossen wurde. Dazu muss folgendermassen vorgegangen werden. Zuerst muss ein select() ausgef…hrt wird, das die angegebenen Sockets auf Lesbarkeit testet. Wenn select() angibt, dass auf einem Socket gelesen werden kann, und es in Wahrheit aber nichts zu lesen gibt, heisst das, dass der Partner_Socket geschlossen worden ist. Connected() verfƒhrt genau auf diese Weise. Das heisst es wird zuerst die oben beschriebene Prozedur Requested aufgerufen, und bei positiver Antwort die noch weiter oben definierte Prozedur Available. Somit kann entschieden werden, ob die Verbindung zwischen dem Socket s und seinem Partner noch steht. SetLinger* (s: LONGINT); Mit setsockopt(s, level, optname, optvalPtr, optlen: LONGINT) k„nnen verschiedene Optionen gesetzt werden. Mit level wird angegeben, auf welcher Protkoll_Ebene die Option interpretiert werden soll. Eine dieser Optionen gibt an was bei der Schliessung einer Verbindung passieren soll. Eine M„glichkeit wƒre die sofortige Schliessung ohne R…cksicht auf Daten, die noch nicht abgeschickt wurden. Mit der Linger_Option wird daf…r gesorgt, dass solche Daten noch abgeschickt werden, bevor die Verbindung geschlossen wird. Die Zeit, wƒhrend der gewartet werden soll, ist …ber einen Record, auf den optvalPtr zeigt, einstellbar. Die folgenden vier Konversions_Prozeduren rufen nur die entsprechenden Sockets Prozeduren htonl(), htons(), ntohl() und noths() auf. Dabei werden auch keine Fehler …berpr…ft und B.done wird immer auf TRUE gesetzt. Da aber externen Prozeduren nur Parameter des Typs LONGINT …bergeben werden k„nnen, und diese Eigenschaft dem Ben…tzer von NetSystem nicht bekannt sein muss, f…hren NetToInt() und IntToNet() noch eine interne Konversion von INTEGER auf LONGINT, und wieder zur…ck, durch. NetToInt* (x: INTEGER): INTEGER; IntToNet* (x: INTEGER): INTEGER; NetToLInt* (x: LONGINT): LONGINT; LIntToNet* (x: LONGINT): LONGINT; Start*; Auf UNIX_Systemen ist diese Prozedur leer. Auf Windows muss hingegen die Sockets_Schicht initialisiert werden, was mit Hilfe von WSAStartup() geschieht. Dabei m…ssen noch die Version der Sockets_Schicht getestet werden, da es Inkompatibilitƒten zwischen den verschiedenen Versionen gibt. Diese Implementation von NetBase erwartet Version 1.1.  Stop*; Auf UNIX_Systemen ist diese Prozedur leer. Auf Windows muss hingegen die Sockets_Schicht genauso oft gestoppt werden, wie sie gestartet wurde. Da mehr als ein Start wenig Sinn macht, halte ich in einer globalen Variablen started fest, ob schon ein Start erfolgt ist. Damit k„nnen unn„tige Starts vermieden werden, und es braucht auch nur einmal gestoppt werden. Init; Hier werden die Prozedur_Variablen an die Bibliotheks_Prozeduren gebunden. Letztere k„nnen sich u.U. in verschiedenen Bibliotheken befinden.  4.2 NetSystem NetSystem stellt zwei verschiedene Arten von Kommunikation zur Verf…gung. Jede Kommunikationsart hat ihren eigenen Kommunikationsendpunkttyp und Satz von Prozeduren. Wer verbindungsorientiert kommunizieren will, muss eine Variable vom Typ NetSystem.Connection ben…tzen. Verbindungslos wird dagegen mit dem Typen NetSystem.Socket gearbeitet. Beide Typen von Kommunikationsendpunkten m…ssen erstmals erstellt werden. Dies geschieht mit NetSystem.OpenConnection bzw. NetSystem.OpenSocket. Ein verbindungsorientierter Server wartet dann …blicherweise bis ein verbindungsorientierter Klient mit NetSystem.Connect eine Verbindung mit ihm aufbauen will. Dazu wartet der Server bis NetSystem.Requested TRUE zur…ckliefert und f…hrt dann ein NetSystem.Accept aus, um die Verbindung anzunehmen. Wenn die Verbindung steht, k„nnen beide mit NetSystem.WriteX (X = Char, Bytes, Int, Bool, String) dem anderen etwas schicken, und wenn NetSystem.Available eine positive Anzahl zu lesender Bytes zur…ckliefert, mit NetSystem.ReadX das entsprechende auch lesen. Mit NetSystem.State kann zusƒtzlich der Status der entsprechenden Connection abgefragt werden. Der Status einer Connection kann folgende Werte annehmen: _ listening: Es handelt sich dabei um den Hauptsocket auf der Serverseite. _ inout: Dies ist des normale Zustand einer Connection …ber die kommuniziert wird. _ in: Die Connection wurde geschlossen, aber es stehen noch ungelesene einkommende Daten an. _ out: Die Connection wurde geschlossen, aber das Senden der letzten Daten ist noch nicht vollendet. _ closed: Auf solch einer Connection kann nicht mehr kommuniziert werden. _ timeout: Ein Verbindungsaufbau hat zulange gedauert und wurde abgebrochen. Bei verbindungsloser Kommunikation wird kein expliziter Unterschied zwischen Server und Client gemacht. Es k„nnen beide mit NetSystem.SendDG einen Datenpuffer schicken und nachdem ihnen NetSystem.AvailableDG bestƒtigt hat, dass ein Datenpuffer angekommen ist und ohne zu blockieren gelesen werden kann, mit NetSystem.ReceiveDG die Daten lesen. Da auf dem Internet die Daten mit einer "big_endian" Byte_Ordnung bef„rdert, und viele Maschinen hingegen mit einer "little_endian" Ordnung arbeiten, gibt es ein Problem wenn Datentypen …bermittelt werden, die gr„sser als ein Byte sind. Deshalb gibt es die Prozeduren NetSystem.PutInt, NetSystem.GetInt, NetSystem.PutLInt, NetSystem.GetLInt, die gegebenenfalls eine Konversion vornehmen, bevor sie die Zahl in den Datenpuffer schreiben. Eine entsprechende Konversion findet auch in den Prozeduren NetSystem.WriteInt, NetSystem.WriteLInt, NetSystem.ReadInt, NetSystem.ReadLInt f…r verbingsorientierte Kommunikation statt. Genauere Beschreibung der einzelnen Prozeduren. (* B=NetBase *)  Start*; _ Liest einen Benutzernamen und ein Passwort ein. Name und Passwort m…ssen durch ein "/" getrennt werden. _ Ruft B.Start() auf. _ Setzt NetSystem.hostname und NetSystem.hostIP mit B.GetHostName() bzw. B.GetHostByName(). Stop*; _ Ruft B.Stop() auf. GetIP* (name: ARRAY OF CHAR; VAR IP: IPAdr); _ Gibt die IP_Adresse der Maschine "name" zur…ck. Falls name eine ASCII_Folge ist, wird B.GetHostByName() aufgerufen. Falls name mit einer Zahl beginnt wird name als IP_Adresse interpretiert, und B.GetHostByIP() aufgerufen. Falls name nichts von dem entspricht, wird anyIP zur…ckgegeben.  GetName* (IP: IPAdr; VAR name: ARRAY OF CHAR); _ Ruft B.GetHostByAdr() und gibt damit den Namen der Maschine mit IP_Adresse IP zur…ck. Falls kein zugeh„riger Name gefunden wird, kommt ein leerer String zur…ck. Verbindungsorientierte Kommunikation: OpenConnection* (VAR C: Connection; locPort: INTEGER; remIP: IPAdr; remPort: INTEGER; VAR res: INTEGER); _ Erstellt einen Kommunikationsendpunkt mit B.Socket(). _ Unterscheidet zwischen Server und Client anhand der remote address oder dem remote port. _ Server: Gibt als Serveradresse anyIP an um multi_homed hosts zu erlauben. Die Portnummer muss mit B.IntToNet() auf Netzwerk Byte_Ordnung gebracht werden. Bindet anschliessend mit B.Bind() die Adresse an den Socket. F…hrt B.Listen() aus, um auf dem Socket Kommunikationsaufforderungen entgegennehmen zu k„nnen. Setzt die linger_Option mit B.SetLinger() und alloziert dann eine Connection. Die Connection wird initialisiert und deren Zustand auf "listening" gesetzt. _ Client: Setzt die Adresse des anzusprechenden Servers und f…hrt B.Connect() aus. Bei Erfolg wird die linger_Option mit B.SetLinger() gesetzt, und die Connection alloziert und initialisiert. Der Status der Connection wird auf inout gesetzt. Die Port_Nummern m…ssen alle mit B.IntToNet() von der Byte_Ordnung des lokalen Rechners in die des Netzwerkes konvertiert werden.  CloseConnection* (C: Connection); _ Ruft B.Closesocket() auf. Requested* (C: Connection): BOOLEAN; _ Sagt, ob eine Verbindungsaufforderung besteht. Diese Funktion ist nur auf der Seite von Serverhauptkommunikationsendpunkten sinnvoll definiert. Ansonsten wird FALSE zur…ckgegeben. Accept* (C: Connection; VAR newC: Connection; VAR res: INTEGER); _ F…hrt auf einer passiven Connection B.Accept() aus. _ Alloziert und initialisiert eine neue Connection, die zur spƒteren Kommunikation dienen wird. State* (C: Connection): INTEGER; _ Liefert den Status der Connection. Dieser kann nur zwei Werte annehmen: inout und close. Openconnection() setzt den Wert auf inout, und CloseConnection() setzt ihn auf close. Zusƒtzlich wird bei jedem Aufruf von State() mit B.Connected() abgefragt, ob die Verbindung schon oder noch steht. GetPartner* (C:Connection; VAR remIP: IPAdr; VAR remPort: INTEGER); _ Liefert die IP_Adresse des Verbindungspartners mit B.Getpeername(). Empfang: Es wurde ein Lesepuffer implementiert, der es erlauben soll Aufrufe an die Socketsschnittstelle zu sparen. Wenn NetSystem.Available() aufgerufen wird, werden automatisch alle vorhandenen Daten in einem Schub in den Empfangs_Puffer der Connection …bertragen. Ohne Puffer w…rde jeder Aufruf von NetSystem.Receive() einen Aufruf der entsprechenden Sockets_Prozedur mit sich ziehen. ReceiveBuffer (C: Connection; VAR buf: ARRAY OF SYSTEM.BYTE; pos: LONGINT; len: LONGINT); _ Liest len Bytes und f…llt sie in den Lesepuffer der Connection. Available* (C: Connection): LONGINT; _ Fragt mit B.Available() die Anzahl vorhandener Bytes ab. _ Ruft danach ReceiveBuffer() auf. Der Parameter len ist das Minimum der tatsƒchlich vorhandenen Daten und der Lƒnge die noch in den Lesepuffer passt. Die Pufferindizes werden dabei nachgef…hrt. _ Gibt die totale Anzahl Bytes an, die gelesen werden k„nnen, ohne zu blockieren. Receive (C: Connection; VAR buf: ARRAY OF SYSTEM.BYTE; beg, len: LONGINT; VAR res: INTEGER); _ Die Daten, die in buf geschrieben werden, kommen zuerst aus dem Lesepuffer der Connection und falls der Benutzer mehr Daten lesen will als im Lesepuffer vorhanden sind, wird u.U. weiter direkt vom Socket gelesen. Senden: Beim Senden wurde hingegen kein Puffermechanismus implementiert, da man sicherstellen will, dass ein Paket das gesendet wird, auch tatsƒchlich losgeschickt wird. Send(C: Connection; VAR buf: ARRAY OF SYSTEM.BYTE; beg, len: LONGINT; VAR res: INTEGER); _ Ruft B.Send() auf.  Send() und Receive() werden nicht exportiert. Dem Benutzer stehen hingegen die folgenden Prozeduren zum Lesen und Schreiben zur Verf…gung. Diese Prozeduren sind typenorientiert, und der Benutzer braucht sich daher keine Gedanken …ber die Gr„sse des gerade …bertragenen Datentyps zu machen.  Read* (C: Connection; VAR ch: CHAR); ReadBytes* (C: Connection; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE); ReadBool* (C: Connection; VAR b: BOOLEAN); ReadInt* (C: Connection; VAR x: INTEGER); ReadLInt* (C: Connection; VAR x: LONGINT); ReadString* (C: Connection; VAR s: ARRAY OF CHAR); Write* (C: Connection; ch: CHAR); WriteBytes* (C: Connection; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE); WriteBool* (C: Connection; b: BOOLEAN); WriteInt* (C: Connection; x: INTEGER); WriteLInt* (C: Connection; x: LONGINT); WriteString* (C: Connection; s: ARRAY OF CHAR); Verbindungslose Kommunikation: OpenSocket* (VAR S: Socket; locPort: INTEGER; VAR res: INTEGER); _ Erstellt einen Kommunikationsendpunkt mit B.Socket(). _ Anschliessend wird mit B.Bind() die eigene Adresse an den gerade erstellten Socket gebunden. Auch hier wird anyIP als eigene Adresse angegeben, um multi_homed hosts zu erlauben. CloseSocket* (S: Socket); _ Ruft B.Closesocket() auf. Empfangen und Senden von Daten: Bei Verbindungsloser Kommunikation werden …blicherweise ganze Datenpakete (DG = Datagram) verschickt. Dabei muss bei jedem Paket die Adresse des Empfƒngers mitgegeben werden. AvailableDG* (S: Socket): LONGINT; _ Fragt mit B.Available() die Anzahl lesbarer Datengrammbytes ab, die gelesen werden k„nnen, ohne zu blockieren. ReceiveDG* (S: Socket; VAR remIP: IPAdr; VAR remport: INTEGER; pos: LONGINT; VAR len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE); _ Liest ein Datenpacket mit B.RecvFrom() ein und schreibt es in die Variable buf ab der Stelle pos. Len zeigt an, wieviel Bytes gelesen wurden. _ Liefert gleichzeitig den Absender des empfangenen Packetes in remIP und remport mit. Dabei muss remport zuvor noch mit B.NetToInt() von der Byte_Ordnung des Netzwerks in die des lokalen Rechners transformiert werden. SendDG* (S: Socket; remIP: IPAdr; remport: INTEGER; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE); _ Schickt ein Datenpacket, das in buf mitgegeben wurde mit B.SendTo() an den, in remIP und remport angegebenen Empfƒnger. Dabei muss ebenfalls mit B.IntToNet() eine Konversion der Byte_Ordnung von remport vorgenommen werden.  Der Puffer, der den beiden Prozeduren SendDG und ReceiveDG mitgegeben wird, kann vom Ben…tzer beliebig gef…llt werden. Da es aber wegen der verschiedenen Byte_Ordnungen Inkompatibilitƒten bei der Uebertragung von Datentypen, die gr„sser als ein Byte sind, geben kann, stehen folgende Prozeduren zur Verf…gung:  PutInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos, x: INTEGER); PutLInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; x: LONGINT); GetInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; VAR x: INTEGER); GetLInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; VAR x: LONGINT); 5 Schwierigkeiten, L„sungen Trotz der vorgeschriebenen Schnittstelle gab es mehrere Implementationsentscheidungen, die ich selber treffen musste. Das erste Problem, das sich stellte, war zu entscheiden was in NetSystem und was in NetBase geh„rte. Einerseits sollte NetSystem portabel sein, und NetBase auf jeder Plattform neu geschrieben werden. Die Tendenz wƒre also, so viel wie m„glich in NetSystem zu packen, und NetBase nur auf das Wesentliche zu beschrƒnken. Andererseits soll NetSystem aber nicht nur von einer Maschine zur anderen portabel sein, sondern auch von der Zeit und neueren Sockets_Versionen verschont bleiben. Die weltweite Umstellung auf neue IP_Adressen ist zwar schwierig und bis jetzt noch ausgeblieben, aber sie scheint doch unvermeidbar. Ich habe mich f…r die Zukunftskompatibilitƒt entschieden, und desshalb auch f…r NetBase ein regelrechtes Interface definiert. Ausserdem habe ich nur drei Versionen von NetBase geschrieben: HP_UNIX, SPARC_Solaris und Windows 3.1. Da ich die Socketsschnittstellen weiterer Systeme (Linux, RS6000, usw.) nicht kenne, wƒre es auch ziemlich riskant gewesen systemnahe Programmierung (Ben…tzung von SYSTEM.VAL, SYSTEM.ADR, SYSTEM.GET, usw.) in NetSystem unterzubringen, da andere Plattformen durchaus verschiedene Parameter oder Datentypen verwenden k„nnten. Als einigermassen klar war, wie sich beide Module die Arbeit teilen w…rden, stellte sich die Frage, ob NetSystem synchron oder asynchron arbeiten sollte. Der Unterschied zwischen synchroner und asynchroner Arbeitsweise lƒsst sich mit der Prozedur receive() sch„n darstellen: Wenn keine Daten vorhanden sind, blockiert ein synchroner Aufruf von receive(), wobei ein asynchroner Aufruf mit einer Fehlermeldung zur…ck kommt. NetSystem Interface sieht beide Arbeitsweisen vor, und lƒsst also dem Implementierer die Wahl. Unter UNIX werden in beiden Fƒllen die gleichen Sockets_Prozeduren aufgerufen und nur mit einem speziellen system_call der entsprechende Socket in einen asynchronen Modus gesetzt. Auf Windows sieht die Sache aber anders aus. Wer asynchron kommunizieren will, muss spezielle Prozeduren aus der API verwenden, die erheblich komplizierter zu handhaben sind. Da ausserdem NetSystem Interface die Prozeduren Available() und AvailableDG() zur Verf…gung stellt, ist es m„glich vor jedem Empfangen abzuklƒren, ob …berhaupt Daten vorhanden sind, und somit ein m„gliches Blockieren im voraus zu vermeiden. Ich habe mich also f…r die synchrone Methode entschieden. Als Nƒchstes kam mir die Idee einen Receive_ und einen Send_Puffer zu implementieren, um die Anzahl Aufrufe der Sockets_Schnittstelle zu minimieren. Den Receive_Puffer habe ich implementiert und er hat mir soweit keine Probleme gemacht. Beim Send_Puffer habe ich mir die Sache aber nochmals …berlegt. Es k„nnte nƒmlich sein, dass der Benutzer diesen f…r ihn unbekannten Puffer f…llt, und, bevor dieser verschickt wird, das Netzwerk zusammenbricht. Die Daten wƒren also ohne Wissen des Benutzers verloren gegangen, was nat…rlich nicht sein darf. Also habe ich mich entschieden, bei jedem Aufruf von NetSystem.Send() oder NetSystem.SendDG() die Daten sofort der Sockets_Schicht zu …bergeben. Es ist mir nat…rlich bewusst, dass auch die Sockets_Schicht die Daten zwischenspeichert. Ein Problem, das ich nicht habe beheben k„nnen, ist mir auf Windows begegnet. Die PCs auf denen ich arbeitete waren - auch nach mehrfacher Nachfrage bei der Stabstelle Software - beim DNS nicht registriert. Um die IP_Nummer einer Maschine zu erfahren wird in NetBase gethostbyname() verwendet, das typischerweise seine Information beim DNS holt. Um die IP_Nummer der eigenen Maschine zu er„rtern hatte ich bis dahin, ohne mir grosse Fragen zu stellen, ebenfalls diese Methode angewendet. Da meine Maschine aber beim DNS unbekannt war, suchte ich nach einem anderen Weg. Unter UNIX bin ich dann mit gethostent() auch f…ndig geworden, suchte aber vergebens nach einer €quivalenten Prozedur im Windows_API - ein klares Manko. Das Windows_API hat mir …brigens noch einige weitere Problemchen vorbereitet. Am Anfang hatte ich nƒmlich auch ziemliche M…he gethostbyname() zum Funktionieren zu bringen, bis sich herausstellte, dass das sonst zwischen Gross_ und Kleinschrift wenig zimperliche System, bei gethostbyname() nur Grossbuchstaben verstand. Jedoch ist es auch m„glich, dass das Problem beim oben erwƒhnten DNS liegt. Wenn die Stabstelle Software ein bisschen kooperativer wƒre ... Doch auch die SPARC Plattform bereitete mir einiges Kopfzerbrechen vor. Auf Sparc_Oberon habe ich zum Beispiel ziemlich lange gebraucht, bis ich einsah, dass gethostname() einfach nicht funktionieren wollte und ich auf sysinfo() ausweichen musste. Das einzige System, wo es keine ‚berraschungen gab war die HP_UNIX Umgebung. 6 Anleitungen zur weiteren Portierung Es muss f…r jede neue Plattform ein Modul NetBase geschrieben werden, das die maschinenabhƒngigen Konstanten, Datentypen und Prozeduren enthƒlt und aufruft. Das Interface, das von NetSystem erwartet wird, ist das folgende:  CONST AFINET; SOCKSTREAM; SOCKDGRAM; IPPROTOUDP; IPPROTOTCP; TYPE IPAdr = ARRAY 4 OF CHAR; Data = ARRAY OF SYSTEM.BYTE; Sockaddrin; VAR done: BOOLEAN; PROCEDURE Accept*(s: LONGINT; VAR newsocket(*out*): LONGINT); PROCEDURE Bind*(s: LONGINT; VAR sockaddr(*in*): Sockaddrin); PROCEDURE Close*(s: LONGINT); PROCEDURE Connect*(s: LONGINT; VAR sockaddr(*in*): Sockaddrin); PROCEDURE GetHostByAdr* (adr: IPAdr; VAR name(*out*): ARRAY OF CHAR); PROCEDURE GetHostByName* (name: ARRAY OF CHAR; VAR addr(*out*): IPAdr); PROCEDURE GetHostByIP* (VAR IPadr(*in*): ARRAY OF CHAR; VAR addr(*out*): IPAdr); PROCEDURE GetHostName* (VAR name(*out*): ARRAY OF CHAR); PROCEDURE GetPeerName*(s: LONGINT; VAR adr(*out*): Sockaddrin); PROCEDURE Listen*(s: LONGINT); PROCEDURE Recv*(s: LONGINT; VAR buf(*out*): ARRAY OF SYSTEM.BYTE; pos: LONGINT; VAR len(*inout*): LONGINT); PROCEDURE RecvFrom*(s: LONGINT; from: Sockaddrin; VAR buf(*out*): Data; pos: LONGINT; VAR len: LONGINT); PROCEDURE Send*(s: LONGINT; VAR buf(*in*): Data; pos: LONGINT; VAR len(*inout*): LONGINT); PROCEDURE SendTo*(s: LONGINT; to: Sockaddrin; VAR buf(*in*): Data; pos, len: LONGINT); PROCEDURE Socket* (VAR s(*out*): LONGINT; af, type, protocol: LONGINT); PROCEDURE Available*(s: LONGINT): LONGINT; PROCEDURE Requested*(s: LONGINT): BOOLEAN; PROCEDURE Connected*(s: LONGINT): BOOLEAN; PROCEDURE SetLinger* (s: LONGINT) PROCEDURE NetToInt* (x: INTEGER): INTEGER; PROCEDURE IntToNet* (x: INTEGER): INTEGER; PROCEDURE NetToLInt* (x: LONGINT): LONGINT; PROCEDURE LIntToNet* (x: LONGINT): LONGINT; PROCEDURE Start*(); PROCEDURE Stop*(); Die Werte der Konstanten hƒngen vom jeweiligen Sockets_Interface ab. Die Typen IPAdr und Data sollten so belassen werden. Sockaddrin sollte hingegen den struct sockaddr_in des zugrundeliegenden Sockets_Interface wiederspiegeln. Die Variable done sollte nach jedem Prozeduraufruf anzeigen, ob dieser erfolgreich war. Die Funktionanlitƒt der einzelnen Prozeduren kann dem Abschnitt 4.1 entnommen werden. Wenn NetBase genau diese Schnittstelle zu NetSystem anbietet, und dar…berhinaus die exportierten Prozeduren das richtige tun, sollte NetSystem funktionsfƒhig sein. 7 Erweiterungen NetSystem_Interface sieht vor, dass gewisse Angaben wie Name und Adresse der lokalen Maschine, der Gateway Maschine oder der verf…gbaren Domain Name Server in einer Konfigurationsdatei stehen. Da NetSystem nun auf der Sockets_Schicht aufbaut, ist es genauso einfach, diese Informationen vom darunterliegenden System zu erfahren. Jedoch k„nnte eine solche Konfigurationsdatei dazu dienen, NetSystem mitzuteilen, ob synchron oder asynchron gearbeitet werden soll. Beide Arten der Kommunikation haben nƒmlich ihre Vor_ und Nachteile. In solchen Fƒllen kann es angemessen sein, dem Benutzer die Wahl zu lassen. Daf…r m…sste aber zuerst die asynchrone Kommunikation implementiert werden, wozu ich nicht mehr gekommen bin. Die €nderungen m…ssten ausschliesslich in NetBase stattfinden, da die Schnittstelle von NetSystem nicht mehr geƒndert werden darf. Unter UNIX ist es ziemlich einfach, einen Socket auf asynchron umzustellen. Das Windows_API sieht f…r asynchrone Kommunikation jedoch eine ganze Palette von speziellen Prozeduren vor, die dar…berhinaus noch viel komplizierter zu handhaben sind als die normalen. Dies kommt davon, dass Windows kein echtes multi_processing System ist, und daher auch keine eingebettete Interprozesskommunikation anbietet. Eine unumgƒngliche Erweiterung werden wohl die neuen 128_Bit IP_Adressen sein. Da auf diesem Gebiet aber noch nichts Konkretes geschehen ist, kann auch ich hier keine Angaben …ber die Natur und den Umfang der n„tigen Zusƒtze oder €nderungen machen. 8 Literaturangaben Die Information, die ich zu dieser Semesterarbeit ben„tigte, habe ich in den folgenden Quellen gefunden: _ [Ste90] : UNIX Network Programming, W.R. Stevens, Prentice Hall, 1990. _ Project Oberon, N. Wirth & J. Gutknecht, Addison_Wesley, 1993. _ The Design of the UNIX Operating System, M. Bach, Prentice Hall, 1989. _ Microsoft Developer's CD. Schliesslich m„chte ich meinem betreuenden Assistenten Jacques Supcik f…r seine stetige Bereitschaft, mir bei den angetroffenen Problemen zu helfen, von Herzen danken.