Files
mdg-filius/tools/java-oracle/README.md
T
julian.kiedaischandClaude Opus 5.5 aa54e5400e feat(editor): F4.6d the web browser and web server on a computer's desktop
The oracle BrowserWindowRun uses Java's browser and web server windows as a
person does; the room keeps a browser window per computer and sends a
program window's view, the start names the person's language. The editor
shows the page in a sandboxed frame, links and forms go to the room, the web
server has its button, virtual hosts and lines. Better than Java: relative
images and links count from the page's folder, a mark stays in the page, the
error page from Java's template, the virtual hosts' file with complete rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:09:05 +00:00

75 KiB
Raw Blame History

Java-Orakel

Das Orakel misst Javas echtes Verhalten, Fehler eingeschlossen. Der TypeScript-Leser folgt Java, übernimmt aber keine Java-Fehler (Datenverlust, Absturz, eigene Ausgabe nicht wieder lesbar, Tippfehler, Verstoß gegen den Standard); jede solche Abweichung steht in den Tests namentlich mit Grund (JAVA_BUGS). Was Java selbst schreibt — der Korpus — wird exakt wie in Java gelesen.

Java-Filius selbst liest den Korpus (corpus/) und schreibt, was es gefunden hat, nach corpus/corpus.java.json. Java ist der Schiedsrichter für das Dateiformat — der Swift-Leser verliert in 10 von 23 Dateien Geräte (Plan §13, M2).

npm run oracle:java     # = tools/java-oracle/run.sh

Braucht JDK 17+, Maven und xvfb-run. Filius öffnet beim Laden sein Hauptfenster (Information.reset → JMainFrame), deshalb läuft es an einem virtuellen Bildschirm statt „headless".

  • Gelesen wird so, wie Filius eine Datei öffnet: SzenarioVerwaltung.laden entpackt das Archiv, kopiert die eigenen Anwendungen (projekt/anwendungen) dorthin, wo Filius' Klassenlader sucht, und lädt dann die Konfiguration — Javas Weg, kein Nachbau (seit 30.09.2026, M8). --network lädt nur die Konfiguration über die private Methode netzwerkLaden (per Reflection), wie vorher. Der Unterschied am Korpus: In anwendung_komplett.fls und multicast_server.fls sind dann die eigenen Anwendungen Accumulator und Broadcast installiert. Filius' Klassenlader ist ein Singleton: Eine einmal geladene eigene Klasse bleibt für die übrigen Dateien desselben Laufs geladen. Am Korpus stört das nicht, denn keine Datei nutzt eine Klasse, die sie nicht mitbringt.
  • Stand: gitlab.com/filius1/filius @ 6640354 (2.14.0), wird beim ersten Lauf nach .upstream/ geholt und gebaut.
  • Gewürfeltes: Was nicht in der Datei steht, würfelt Filius zum Teil (MAC-Adressen, die SSID eines Switches). Jede Datei wird deshalb zweimal geladen; jeder Wert, der sich zwischen beiden Läufen unterscheidet, erscheint als null. Zwei Läufe des Werkzeugs sind byte-gleich. (Bis 29.09.2026 suchte es die MAC im Dateitext und hielt dabei acht kleingeschriebene für gewürfelt.)
  • Bisher ausgelesen: Geräte (Klasse, Name, Beschriftung, Position, Schnittstellen samt Funk-Eigenschaft wireless, bei Rechner/Notebook useIPAsName/useMACAsName), unter system SSID, Lernzeit des Switches, RIP, manuelle Routen, die Firewall von Router und Heimrouter, DHCP-Client und -Server, die statische NAT-Tabelle des Heimrouters, die Verbindung eines Modems und das Dateisystem (Inhalte als Länge und SHA-256, damit die Referenz klein bleibt; bei Router und Heimrouter stehen vorn die Dateien, die Javas Konstruktor aus eigenen Vorlagen erzeugt, dahinter die aus der Datei) und die installierten Programme eines Rechners mit ihren Einstellungen (die persönliche Firewall samt Regeln; je Programm onItsSystem: ob es am Betriebssystem seines Knotens hängt) (Klassen, die Filius nicht mitbringt — eigene Anwendungen im Archiv unter projekt/anwendungen — fehlen, weil das Werkzeug nur konfiguration.xml lädt); E-Mail: Konten des Servers aus der Bean-Liste und aus /mailserver/konten.txt (kontenLaden), Ordner und Adressbuch des Clients und sein Konto aus /konten.txt (laden); ein Empfänger, den eine ältere Datei als Zeichenkette hält, samt dem, was Javas AddressEntry(String) daraus macht; Kabel (Enden als Gerät/Schnittstelle), Doku-Elemente.

Getter-Standardwerte

XMLEncoder teilt gleiche Integer/Boolean-Objekte. Ein Wert, der einmal als Ergebnis eines Getters geschrieben wurde (<void id="Integer0" property="iconTextGap"/>), taucht später nur noch als Verweis auf — etwa als x-Koordinate eines Geräts. Daran scheitert der Swift-Leser (Plan §13, M2).

GetterDefaults erzeugt die betroffenen Klassen wie XMLDecoder (ohne Argumente) und ruft den Getter auf; das Ergebnis steht in packages/fls/src/javaDefaults.json. Gemessen, nicht geraten: Filius' Knopf setzt verticalTextPosition auf 3 (BOTTOM), nicht auf Swings 0 (CENTER).

Dasselbe gilt für statische Konstanten: Die erste 0 einer Datei kann als <object class="javax.swing.SwingConstants" field="CENTER"/> stehen, und jede spätere 0 — etwa die Rot-, Grün- und Blauwerte einer schwarzen Doku-Farbe — verweist darauf. Schreibweise im Aufruf: klasse::FELD, Ergebnis "SwingConstants.CENTER": 0.

IP.ipCheck

Java-Filius schickt jede IP-Adresse und Maske beim Laden durch IP.ipCheck. IpCheckCases ruft es für jede Zeile von packages/fls/test/ip-check.cases.txt auf (ASCII, Nicht-ASCII als \uXXXX) und schreibt ip-check.java.json. Gemessen: leere Abschnitte zählen nicht (10..0.0.1), Vorzeichen und führende Nullen gehen, ebenso Ziffern jeder Schrift in der BMP (Thai, Devanagari, Vollbreite) — aber kein Leerzeichen und kein 𝟏 (Ersatzpaar).

/dns/hosts

Javas DNS-Server liest seine Einträge bei jeder Anfrage aus der Textdatei /dns/hosts. DnsHostsCases legt für jede Zeile von packages/fls/test/dns-hosts.cases.txt (eine ganze Datei je Zeile, ASCII, Zeilenumbruch als \u000A) einen Rechner mit dieser Datei an und schreibt, was DNSServer.holeResourceRecords() liefert, nach dns-hosts.java.json. Gemessen: Felder trennt nur ein einzelnes Leerzeichen (Tab und NBSP nicht), Zeilen nur LF; ein Name, den Javas Muster ablehnt (1und1.de, müller.de), wird leer, der Eintrag bleibt trotzdem; Typen außer A/MX/NS (auch a, CNAME, eine Kommentarzeile) gelten als gültig; eine TTL, die kein int ist, wird still 3600.

Was Java aus unseren Dateien lädt

run.sh lädt auch die Dateien unter packages/fls/test/written/, die topologyWriter.test.ts mit UPDATE_WRITTEN=1 schreibt, und unter packages/fls/test/exported/, die exportProject.test.ts mit UPDATE_EXPORTED=1 schreibt (beide nicht eingecheckt), mit derselben Lademethode wie den Korpus, und schreibt written.java.json bzw. exported.java.json daneben, samt SHA-256 jeder Datei (--sha256), damit der Test merkt, wenn der Befund nicht mehr zur Ausgabe passt. Je Kabel steht auch panel: die Geräte, zwischen denen die Oberfläche es zeichnet (JCablePanel.ziel1/ziel2) — im Korpus bei allen 183 Kabeln die Geräte, die es verbindet.

Wie Java Base64 schreibt

Base64Cases ruft Filius' eigenes Base64.encodeBytes (iHarder 2.x) auf Byte-Folgen von 19 Längen rund um die Zeilenlänge auf (packages/fls/test/base64.java.json). Befund: nach je 76 Zeichen ein \n, auch nach der letzten vollen Zeile, dann der aufgefüllte Rest ohne Umbruch. So stehen Bilder in den Dateien, auch die, die Javas Konstruktoren selbst anlegen.

Was Java aus unserem XML macht

BeansDump liest jede *.xml unter packages/fls/test/beans/ mit XMLDecoder und beschreibt, was dabei herauskommt (Beans über ihre Eigenschaften mit Lese- und Schreibmethode, Listen, Maps, Arrays, Rectangle, Color; ein zweites Mal gesehenes Objekt als Verweis) — beans.java.json. Fehler, die der Decoder sonst nur ins Log schreibt, sammelt ein ExceptionListener. Zeichenketten stehen als ASCII mit \uXXXX, weil ein einsames Surrogat in UTF-8 sonst zu „?“ würde.

E-Mail-Kontodateien

EmailFileCases server|client legt für jede Zeile von packages/fls/test/email-server.cases.txt bzw. email-client.cases.txt einen Rechner mit dieser Datei an (/mailserver/konten.txt bzw. /konten.txt) und schreibt, welche Konten EmailServer.kontenLaden() bzw. EmailAnwendung.laden() daraus machen. Gemessen: Eine einzige Serverzeile mit weniger als fünf Feldern lässt Java die ganze Datei verwerfen (NoSuchElementException — die Simulation startet nicht); leere Felder überspringt Java, statt sie zu zählen; eine Nachricht mit leerem Text geht verloren, weil split leere Felder am Ende abschneidet. Beim Client gewinnt die letzte Zeile, auch eine kaputte, die das Konto leert.

DHCP zur Laufzeit

DhcpRun baut das Netz eines Kern-Szenarios (packages/core/test/scenarios/*.scenario.json: Knoten, Kabel, Geräte, DHCP-Server und -Clients; die Schritte bleiben unbeachtet) in Java-Filius nach und startet es wie der Aktionsmodus (starten() jeder Systemsoftware). Nach der angegebenen Echtzeit schreibt es nach <name>.java.json, was jede Netzwerkkarte gesehen hat (Filius' eigener Mitschnitt, der Lauscher: Zeit, MACs, ARP-Operation und -Adressen, IP/UDP-Adressen und -Ports samt DHCP-Text), und die Adressen jedes Rechners am Ende. Die MAC eines Ports ist dieselbe wie im Kern (mac, sonst stableGeneratedMacAddress).

  • Switches und DHCP-Server starten zuerst: Javas Client wartet auf jeden aktiven Server, findet sie aber über die Knotenliste der Oberfläche, die das Werkzeug nicht hat.
  • GUIContainer.getGUIContainer() wird vor dem Start einmal aufgerufen. Im echten Filius steht das Fenster schon; ohne das legen zwei Clients den Singleton gleichzeitig an (nicht synchronisiert), und einer von ihnen sendet nie etwas (gemessen am Szenario dhcp-lan).
  • Die Zeiten schwanken von Lauf zu Lauf um einige zehn Millisekunden (echte Threads), die Link-Local-Adresse würfelt Java mit Math.random. Die Reihenfolge der Nachrichten eines Clients und ihr Inhalt schwanken nicht.

Gemessen (30.09.2026), was Swift anders macht: Je Versuch sendet Java genau ein DISCOVER bzw. REQUEST und wartet dann bis zu einer RTT (2,5 s) auf die Antwort; eine unpassende Antwort (etwa das Angebot des zweiten Servers) liest es und verwirft sie. Vor dem REQUEST prüft es die angebotene Adresse per ARP und wartet dabei eine RTT; antwortet ein anderer Rechner, lehnt es mit DHCPDECLINE ab. Javas Server übergeht das DECLINE und hält die Adresse weiter als Angebot fest (4 RTT). Nach dem fünften Fehlversuch (Zeitablauf) nimmt der Client eine Link-Local-Adresse 169.254.1.x/16: ARP-Probe, eine RTT warten, dann ein ARP-Reply an alle.

Die RTT ist in Java nicht fest: Verbindung.holeRTT = 50 Kabel × Verzögerung je Kabel × RTT-Faktor (Startoption, 1). Beim Anlegen des Fensters setzt das Hauptmenü den Regler auf 90 %, also 55 ms je Kabel und 2750 ms RTT — so laufen die DhcpRun-Referenzen; gemessen sind die Wartezeiten 2745 bis 2751 ms. Außerdem befördert ein Java-Kabel einen Frame nach dem anderen (SimplexVerbindung schläft die Verzögerung mit gesperrtem Puffer): Sendet eine Karte zwei Frames direkt nacheinander, kommt der zweite eine Kabellänge später.

Filius' Mitschnitt selbst ist dabei nicht ganz zuverlässig: Starten zwei Clients zugleich, fehlt auf der Karte des zweiten dessen eigenes DISCOVER (vier von vier Läufen von dhcp-lan; der Server empfängt es).

TCP zur Laufzeit

TcpRun spielt auf dem Netz von tcp-lan (Server 192.168.0.10, Clients .20 und .30) einen festen Ablauf von Socket-Aufrufen ab, wie Filius' Anwendungen sie machen, den Server in einem eigenen Thread. Es schreibt nach tcp-java-<ablauf>.java.json die Frames jeder Karte (mit TCP-Flags, Sequenz- und Bestätigungsnummer), wann jeder Aufruf begann, zurückkam oder scheiterte, und jeden Zustand jedes Sockets (alle 2 ms abgefragt). Netzbau und Mitschnitt teilen DhcpRun und TcpRun in ScenarioNetwork.

  • exchange: Verbindung zu Port 80, GET /, Antwort 3000 × x (drei Segmente), der Client schließt, der Server eine Sekunde nach CLOSE_WAIT.
  • closedPort: Verbindung zu Port 81, auf dem niemand lauscht.
  • lostData: verbunden, dann gehen alle Frames verloren (Verbindung.setDrop, wie der Paketverlust des Kerns), während der Client sendet; der Verlust endet, wenn senden scheitert.
  • lostSynAck: das erste SYN+ACK des Servers geht verloren.
  • lostFin: verbunden, der Client schließt; sein FIN geht verloren.
  • lostLastFin: verbunden, der Client schließt; das FIN des Servers geht verloren.

Einen einzelnen Frame verliert loseFirst: Per Reflection ersetzt es den Ausgangspuffer der Karte (Port.ausgangsPuffer) durch einen, der den ersten passenden Frame verwirft, statt ihn einzureihen. Das Kabel wartet am alten Puffer und wird dort einmal geweckt. Der Lauscher des Absenders sieht den Frame wie bei setDrop (er schreibt nach dem Einreihen); anders als dort belegt der Frame das Kabel nicht.

Gemessen (30.09.2026, 90 %, RTT 2750 ms), was Swift anders macht:

  • Jeder Versuch wartet 3 RTT (8250 ms): Daten gehen dreimal hinaus (1720, 9970, 18220 ms), senden scheitert 8250 ms nach dem dritten.
  • Scheitert senden, schließt Java ordentlich: FIN, ACK, FIN, ACK. Swift setzt den Socket ohne FIN auf CLOSED.
  • TIME_WAIT dauert 3 RTT (8250 ms; oder bis zum nächsten Segment), nicht eine RTT.
  • Die Gegenseite schließt von selbst: Kommt ein FIN, geht Javas Socket-Thread aus listen in close und sendet sein FIN sofort (LAST_ACK rund 60 ms nach dem FIN), nicht erst, wenn die Anwendung schliessen aufruft.
  • Der Client sendet nur ein SYN: Nach dem ersten Zeitablauf setzt connectClientMode closeSocket, und die Schleife über drei Versuche endet (verbinden scheitert nach 3 RTT). Der Server-Socket wiederholt sein SYN+ACK nie.
  • Nach einem verlorenen SYN+ACK gibt der Server nach 3 RTT auf und schickt aus SYN_RCVD ein FIN (close). In zwei von drei Läufen wartete er 6 RTT: Der Vergleich vergangen > defaultTimeout() liegt genau auf der Grenze.
  • Ein FIN geht einmal hinaus, nie wieder. Danach wartet close höchstens fünfmal 3 RTT; jedes ankommende Segment beendet eine Wartezeit. Dann ist der Socket weg, sein Zustand bleibt aber stehen (FIN_WAIT_1, FIN_WAIT_2, LAST_ACK). Die Gegenseite eines verlorenen FIN bleibt ESTABLISHED.
  • Startnummern aus einem Zähler über alle TCP-Sockets (1000000, 2000000, …), wie im Kern; der freie Port ist zufällig.

DNS zur Laufzeit

DnsRun ruft auf dem Netz von dns-lan den Resolver eines Clients auf, wie Filius' Programme es tun, und schreibt nach dns-java-<ablauf>.java.json die Frames jeder Karte und das Ergebnis des Aufrufs. ScenarioNetwork richtet dazu die dnsServers des Szenarios ein: installiert, Einträge über hinzuRecord in /dns/hosts (Zeilen name TYP ziel, TTL 3600), eingeschaltet. Server 192.168.0.10 kennt web.filius.de, den Mailserver von filius.de und verweist schule.de an 192.168.0.11, der www.schule.de kennt.

  • address, mail, referral, unknown: A, MX (samt A des Mailservers), Verweis per NS, unbekannter Name.
  • noServer: der DNS-Server des Clients (192.168.0.99) fehlt.

Gemessen (30.09.2026), Javas Format, das Swift durch ein eigenes ersetzt: Jede Nachricht ist Text, ID=… QR=… RCODE=… QDCOUNT=… ANCOUNT=… NSCOUNT=… ARCOUNT=… und dann je Zeile eine Frage name. TYP IN oder ein Eintrag name. TYP ttl ziel. Die ID würfelt Java (Math.random). Die Antwort wiederholt die Frage nicht (QDCOUNT=0), RCODE ist immer 0, auch für einen unbekannten Namen (leere Antwort), und ein Verweis steht mit NS und A des Nameservers im Antwortteil. Der Resolver folgt dem Verweis selbst (iterativ) und wartet je Anfrage 5 RTT (13750 ms bei 90 %); ohne Server fragt ARP zweimal im Abstand einer RTT, dann scheitert der Aufruf.

Auf dem Netz von dns-recursive (sonst wie dns-lan) löst 192.168.0.10 rekursiv auf, sein eigener DNS-Server ist .11, und er führt die Zone filius.de mit sich selbst als Nameserver. Abläufe (dns-recursive-java-<ablauf>.java.json): address (eigener Eintrag), referral (www.schule.de über den NS von schule.de), unknown (x.unbekannt.org), unknownInZone (nix.schule.de, das .11 nicht kennt), unknownInOwnZone (nix.filius.de).

Gemessen (30.09.2026): Findet der Server nichts in seinen Einträgen, fragt er selbst, mit seinem eigenen Resolver (iterativ, 5 RTT je Anfrage): zuerst den Nameserver der längsten passenden Zone aus seinen Einträgen, bringt das nichts, seinen eigenen DNS-Server (nur wenn das nicht er selbst ist). Die Antwort an den Client enthält, was er gelernt hat, sonst den Verweis (NS und A). Bei unknownInZone fragt er .11 also zweimal. Bei unknownInOwnZone fragt er als Nameserver der Zone sich selbst, ohne Ende: Jede Ebene erzeugt die nächste (ohne Frames, die Anfragen bleiben im Rechner), der Client gibt nach 5 RTT auf, dann laufen die inneren Ebenen nacheinander ab und fragen ersatzweise .11, eine Anfrage alle 55 ms. Ein Java-Fehler; der Kern fragt sich weder als Nameserver noch als DNS-Server selbst.

ARP zur Laufzeit

ArpRun sendet auf dem Netz von arp-routed (10.0.0.2 über einen Switch am Router 10.0.0.1, 20.0.0.2 am Router 20.0.0.1; ScenarioNetwork baut dazu Router mit setzeAnzahlAnschluesse und den interfaces des Szenarios) an Adressen, die keine Karte hat, und schreibt nach arp-java-<ablauf>.java.json die Frames und die Aufrufe. Abläufe: pingLan (Ping an 10.0.0.9, ICMP.sendProbe), pingRouted (Ping an 20.0.0.9), udpTwice (zwei Datagramme an 10.0.0.9), udpBehind (eines an 10.0.0.9, dann eines an 20.0.0.2), routedUdpBehind (eines an 20.0.0.9, dann eines an 20.0.0.2: der Router leitet beide weiter), routedPingBesideUdp (Ping an 20.0.0.2, damit der Router es kennt, ein Datagramm an 20.0.0.9, 100 ms später wieder ein Ping an 20.0.0.2) und routedPingBehindPing (ein Echo-Request an 20.0.0.9 ohne Warten auf Antwort, dann ein Ping an 20.0.0.2). ICMP-Frames schreibt ScenarioNetwork mit Typ, Code, Adressen und Sequenznummer.

Gemessen (30.09.2026), was der Kern (nach Swift) anders macht:

  • ARP fragt bis zu zweimal und wartet je eine RTT (holeARPTabellenEintrag(ziel, 2, …)); dann „Host unreachable" (ICMP 3/1). Einen Negativ-Cache gibt es nicht: das nächste Paket fragt wieder zweimal.
  • Wer wartet, ist ein Thread: beim Ping der Aufrufer (5,5 s, dann ICMP 3/1 vom eigenen Rechner, Absender 127.0.0.1), beim Weiterleiten der IP-Thread des Routers, bei UDP und TCP der Transport-Thread je Protokoll und Knoten. senden kehrt sofort zurück, aber der Transport-Thread hält seine Warteschlange gesperrt, solange er wartet: das nächste senden blockiert bis dahin (5,5 s), und ein Datagramm an einen erreichbaren Rechner geht erst danach.
  • Ein Ping wartet eine RTT auf die Antwort; das „Host unreachable" des Routers (nach dessen 5,5 s) kommt zu spät.

Gemessen (30.09.2026) an den Router-Abläufen:

  • Der Router leitet UDP und TCP in seinem IP-Thread weiter, ICMP in seinem ICMP-Thread (EthernetThread sortiert nach Protokoll in zwei Puffer). Beide halten ihren Puffer, solange ARP wartet: Ein Datagramm an 20.0.0.2 wartet 5,5 s hinter einem an 20.0.0.9, ein Echo-Request ebenso hinter einem Echo-Request (der Ping kehrt nach einer RTT ohne Antwort zurück, die Antwort kommt später). Ein Ping neben einem hängenden Datagramm geht sofort durch. Auch das Echo-Reply eines Rechners schickt sein ICMP-Thread.
  • Javas ARP weckt mit arpTabelle.notify() nur einen Wartenden, nicht den, dessen Antwort kam. Warten zwei Threads (im Rechner der UDP-Thread und der Ping-Aufrufer, im Router IP- und ICMP-Thread), wartet der andere bis zum Ende seiner RTT; einer, der fälschlich geweckt wurde, verbraucht einen Versuch und fragt sofort wieder (gemessen: das zweite ARP nach 371 ms statt 2750, der ICMP-Thread schickte den Echo-Request eine RTT zu spät). Ein Java-Fehler, den der Kern nicht übernimmt: Er weckt alle, die auf die Adresse warten. Die Abläufe meiden ihn (nur ein Wartender zur Zeit).
  • Ein Datagramm, das senden dem UDP-Thread übergibt, geht oft erst nach einem Frame hinaus, den der Aufrufer gleich danach selbst schickt (der Thread wacht später auf). routedPingBesideUdp wartet deshalb 100 ms.

Der Kern spielt alle sieben Abläufe wie Java (packages/core/test/arpJava.test.ts: Frames, Abstände und die Zeiten, zu denen die Aufrufe zurückkehren). Die UDP-Abläufe seit F2.4b-2: ein Senden kehrt sofort zurück wie in Java (Swifts Senden wartete, bis alle Frames angekommen waren), und je Knoten und Protokoll sendet ein Transport-Thread ein Segment nach dem anderen.

RIP zur Laufzeit

RipRun startet das Netz von rip-chain (PC 10.0.1.2 – R1 – R2 – R3 – PC 10.0.3.2, jeder Router mit RIP; ScenarioNetwork schaltet RIP für die im Feld rip genannten Router ein) und schreibt nach rip-java-<ablauf>.java.json die Frames, jede Änderung jeder RIP-Tabelle (alle 2 ms abgefragt, Zeile netz/maske gateway schnittstelle hops; …) und die Aufrufe. Java würfelt die Pausen eines Routers aus einem neuen Random; RipRun setzt jedem Router per Reflection (VermittlungsrechnerBetriebssystem.ripbeacon, RIPBeacon.rand) den Startwert, den der Kern ihm gibt (ripSeed: Startwert des Szenarios, 0x52495000 und FNV-1a der Router-ID). Dann würfeln beide dieselben Pausen.

  • converge: die Tabellen füllen sich, bei 45 s pingt 10.0.1.2 10.0.3.2.
  • expire: bei 10 s schweigt R3 (RIPTable.setNextBeacon(Long.MAX_VALUE)), bei 125 s der Ping noch einmal.

Gemessen (30.09.2026, dazu die Pausenformel am JDK):

  • Die Pause ist (int) (30000 * (rand.nextFloat() / 3 + 0.84)); nextFloat() / 3 rechnet Java in float, in etwa einem von 10.000 Fällen eine Millisekunde anders als in double.
  • Eine gelernte Route bekommt 16 Hops erst beim nächsten Beacon ihres Routers nach dem Verfallen (RIPTable.check): R3s Netz verfiel bei R2 nach rund 76,4 s, R2 setzte es bei seinem Beacon um 86,7 s auf 16; R1 hörte die 16 vom selben Router und meldete sofort. Der Ping bekommt dann ICMP 3/0 von R1. Der Kern setzt die Route seit 01.10.2026 genau beim Verfallen auf 16 und meldet sofort (besser als Java, wo Java nur bei seinem Takt nachsieht); ripJava.test.ts vergleicht expire bis dahin und prüft danach, dass R2 zur Zeit und R1 ein Kabel später auf 16 steht (BETTER_THAN_JAVA_FROM).
  • Die Router starten nacheinander, rund 20 ms auseinander; wer einen Nachbarn vor seinem ersten Beacon hört, sendet die gelernte Route gleich mit. Senden mehrere zugleich, fehlt im Lauscher mancher eigene Frame.

NAT zur Laufzeit

NatRun startet das Netz von nat-home (PC 192.168.0.10 am LAN-Port des Heimrouters 192.168.0.1; sein WAN-Port 42.0.0.10/8 an einem Switch mit 42.0.0.20 und 42.0.0.30; Portweiterleitung TCP 8080 an 192.168.0.10:80; ScenarioNetwork baut Heimrouter und das Feld portForwarding über GatewayFirmware.setStaticNAT) und schreibt nach nat-java-<ablauf>.java.json die Frames, die Aufrufe und am Ende die NAT-Tabelle des Heimrouters. Abläufe: pingOut, udpOut (Antwort vom Server), udpTwoRemotes (danach vom selben Port an 42.0.0.30), tcpOut, forward (von 42.0.0.20 an 42.0.0.10:8080), pingWan (42.0.0.20 pingt die WAN-Adresse, dann 192.168.0.10).

Ein UDP-Server im Werkzeug ist ein aktiver Socket auf Port 7: ein passiver trägt seinen Port erst ein, wenn Daten da sind, die vorher nicht ankommen.

ScenarioNetwork schreibt seit F2.6 die Frames jeder Karte eines Knotens (die erste unter der Knoten-ID, weitere unter id#index); vorher nur die erste, so dass bei Routern und Heimroutern die zweite Karte fehlte. Die ARP-, RIP- und NAT-Läufe sind damit neu aufgenommen; die übrigen Knoten haben nur eine Karte.

Gemessen (30.09.2026), was der Heimrouter tut (NatGateway, dessen Threads zwischen Karte und Ethernet-Schicht sitzen):

  • Ausgehend (Ziel außerhalb des LAN, nicht die WAN-Adresse) wird die Absenderadresse zur WAN-Adresse, der Port zu einem freien Port des Heimrouters (nach Javas Zufall); ICMP behält seine Kennung, die als Port des Eintrags dient. Einen Eintrag legt ein TCP-SYN, jedes UDP-Datagramm und jedes ICMP-Paket an, aber nur, wenn der LAN-Endpunkt noch keinen hat.
  • Eingehend an die WAN-Adresse sucht der Heimrouter den Eintrag nach Gegenstelle, WAN-Port und Protokoll, sonst nach der Portweiterleitung, die dann einen dynamischen Eintrag für die Gegenstelle anlegt (DynamicEnryFromStatic).
  • Ein zweiter Server, an den derselbe LAN-Endpunkt sendet, bekommt das Datagramm mit demselben WAN-Port, seine Antwort aber findet keinen Eintrag (der gilt nur für die erste Gegenstelle) und kommt nicht ins LAN.
  • Ein Ping an die WAN-Adresse beantwortet der Heimrouter, einer an eine LAN-Adresse von außen verschwindet.
  • Dynamische Einträge verfallen nach 300 s mal Javas Verzögerungsfaktor (bei 90 % 55 Minuten), geprüft jede Sekunde, aufgefrischt nur von eingehenden Paketen (aus dem Code, nicht gemessen). Der Kern verfällt sie seit 01.10.2026 genau in diesem Alter statt beim nächsten Sekundentakt.

WLAN zur Laufzeit

WlanRun startet das Netz von wlan-lan (Notebooks 192.168.1.11 und .12 mit Funkkarte, je über ein Funkkabel am Switch mit der SSID „schule"; PC 192.168.1.13 an einem Kabel; Notebook 192.168.1.14 mit Funkkarte und SSID „schule", aber ohne Kabel) und schreibt nach wlan-java-<ablauf>.java.json die Frames und die Aufrufe. ScenarioNetwork setzt dazu Kabel.setWireless nach dem Feld wireless eines Kabels, die SSID des Switches (switches) und Funkkarte und SSID der Rechner (wireless). Abläufe: pingWired (.11 pingt .13), pingWireless (.11 pingt .12), pingWithoutCable (.11 pingt .14), pingFromWithoutCable (.14 pingt .11).

Gemessen (01.10.2026), wie im Code (Kabel.wireless, Betriebssystem.ssid liest nur die Oberfläche):

  • Ein Funkkabel ist zur Laufzeit ein Kabel wie jedes andere: dieselben Frames zu denselben Zeiten über Funk wie über Kabel (Ping 470 ms).
  • Eine SSID verbindet nichts. Das Notebook ohne Kabel bleibt unerreichbar (zweimal ARP, nach 5,5 s „Host unreachable" beim Absender), und es selbst erreicht niemanden. Das Funkkabel legt nur Javas Editor (JHostKonfiguration.updateWifiConnection: das alte entfernen, an den Switch mit der SSID anschließen); ändert sich die SSID eines Switches, entfernt er dessen Funkkabel, ohne neu zu verbinden. So entsteht in einer Datei ein Rechner mit SSID ohne Kabel.

Swift verbindet zur Laufzeit jeden Rechner mit eingeschaltetem WLAN mit einem freien Port des Switches seiner SSID, auch ohne Kabel (wirelessAssociations). Der Kern folgt Java: Er kennt nur die Kabel.

Mitschnitt je Karte

Was ScenarioNetwork.writeFrames schreibt, ist Javas Mitschnitt selbst (Lauscher.datenEinheiten): je Karte jeder Frame, den sie gesendet oder empfangen hat. Seit F2.8a vergleicht der Kern seinen Mitschnitt Karte für Karte damit (expectSameCaptures in packages/core/test/javaRun.ts), nicht mehr nur jeden Frame dort, wo er zuerst auftaucht.

Dabei fiel auf (01.10.2026): ScenarioNetwork übernahm die Lernzeit eines Switches (switches.<id>.retention) nicht; Java lief mit 300 s, der Kern mit den 5 s der Szenarien tcp-lan und dhcp-*. Der alte Vergleich sah den Unterschied nicht: Ein gefluteter Frame erscheint nur auf zusätzlichen Karten. Seit F2.8a setzt das Werkzeug die Lernzeit (SwitchFirmware.setRetentionTime), die TCP- und DHCP-Läufe sind neu aufgenommen. Mit 5 s flutet auch Javas Switch in lostData den FIN nach 26 s an alle Karten, wie der Kern. dhcp-lan behält die alte Aufnahme: Die neue unterscheidet sich nur darin, welcher der beiden Clients zuerst sendet; nach 5 s gehen dort nur noch Broadcasts.

Javas Switch prüft seine Tabelle einmal je Sekunde ab dem Start (SwitchFirmware.checkSAT), ein Eintrag verfällt also bis zu einer Sekunde nach der Lernzeit; der Kern genau bei ihr. Das ist gewollt und wird nicht gemessen: Wo Java nur in einem Takt nachsieht, macht der Kern es genauer (Entscheidung 01.10.2026; dasselbe für die NAT-Einträge des Heimrouters).

Tabelle des Mitschnitts

Seit F2.8b schreibt ScenarioNetwork.writeFrames neben den Frames Javas fertige Tabelle jeder Karte (Lauscher.getDaten): rows mit Adresse:Port (das Fenster „Datenaustausch"), layeredRows nur mit Ports (die Schichtenansicht); je Zeile Nummer, Quelle, Ziel, Protokoll, Schicht, Bemerkung, ohne die Uhrzeit. Java läuft dabei mit Locale.GERMANY (Information.getLocaleOrDefault): deutsche Wörter, Tausenderpunkte. Alle Laufzeit-Läufe sind dafür neu aufgenommen (bis auf dhcp-lan, s. oben); NatRun hat den Ablauf ttlOut (Ping mit TTL 1 aus dem LAN: der Heimrouter meldet „Zeit überschritten").

Gemessen (01.10.2026), wo der Kern abwich:

  • Eine ARP-Anfrage trägt als Ziel-MAC FF:FF:FF:FF:FF:FF (ARP.sendeARPBroadcast), der Kern trug Nullen (wie Swift).
  • Eine ICMP-Fehlermeldung trägt Identifier und Sequenznummer 0, der Kern kopierte die des Echos (wie Swift).
  • „Host nicht erreichbar" vom Router trägt das Paket mit gesenkter TTL (wie er es weitergeleitet hat), der Kern das angekommene.

Bewusst nicht übernommen: Javas Antwort an 0.0.0.0 schreibt die Broadcast-Adresse klein (ARP.sendArpReply), der Kern immer groß; „ICMP Time Exeeded" ist ein Tippfehler.

RIP: Der Anlauf entscheidet, wie viele Zufallspausen jeder Router zieht (jede Nachricht zieht eine), und der hängt an Javas Startreihenfolge. Eine Aufnahme, deren Anlauf von dem des Kerns (alle Router zugleich) abweicht, liegt danach in allen Zeiten daneben; das Werkzeug wird dann wiederholt.

Beispiele von Filius (Abnahme F2)

ExampleRun öffnet eine Beispieldatei aus corpus/java-examples so, wie Filius eine Datei öffnet (ScenarioNetwork.fromFile: SzenarioVerwaltung.laden in die Listen des Fensters), startet sie wie der Aktionsmodus (jedes Gerät in der Reihenfolge der Datei), spielt einen Ablauf und schreibt nach packages/fls/test/examples/<ablauf>.java.json die Frames jeder Karte (unter ihrer MAC-Adresse), Javas Tabellen, die Zuordnung der Karten (cards: MAC → Gerät/Index, ein wiederholter Name mit #2, #3, …), die Aufrufe, die Adressen am Ende und jede Änderung der RIP-Tabellen. Abläufe: routing (internetwork_komplett: Pings über drei Router, dann von einem Notebook ohne Gateway), dhcp (einfaches_rechnernetz_komplett: vier Clients), dns (dns_server: rekursiv über Wurzel, de., filius.de.), nat (nat_with_mapped_port: Pings durch zwei Heimrouter), firewall (Internet_Komplett_…_DE: acht DHCP-Clients, dann Pings durch die Firewall).

Gemessen (01.10.2026):

  • ipForwardingEnabled eines Rechners: Java speichert und beachtet den Schalter (meine-schule.de leitet die Pakete der iPads weiter); Leser, Modell, Kern und FlsOracle kannten ihn nicht. FlsOracle schreibt ihn jetzt für jeden Knoten mit IP-Stapel; Korpus, Schreiber und Export neu geprüft.
  • ARP-Lernregel (ARPThread): Einen Eintrag lernt ein Gerät nur aus einer Anfrage nach seiner eigenen Adresse oder aus einer Antwort, eine Antwort gibt es nur auf eine Anfrage mit Ziel-MAC ff:ff:ff:ff:ff:ff, und ein Paket von der Netzadresse wird verworfen. Der Kern lernte aus jedem Paket.
  • Javas Firewall wendet ihre Regeln nur auf TCP und UDP an; ICMP lässt sie durch, außer „ICMP verwerfen" ist an (checkAcceptIcmp). Der Kern ebenso.
  • Ein Ping von einem Rechner ohne Gateway an ein fremdes Netz wirft in Java eine NullPointerException; der Kern meldet „Netz nicht erreichbar".
  • Gleich lange RIP-Wege: Hat ein Router zwei gleich lange Wege, nimmt Java den, dessen Nachricht seine Threads zuerst verarbeiten; im Firewall-Beispiel wechselte das zwischen Läufen (Router #3 zu 42.0.0.0 über Router "" oder über #2). Der Ablauf meidet solche Ziele.
  • Gleichzeitig startende DHCP-Clients bedient der Server in der Reihenfolge, in der Javas Threads senden; mit acht Clients stauen sich die Broadcasts, ein Frame erreicht die Karten bis zu rund 450 ms versetzt, und Javas Mitschnitt verliert eigene Frames eines Clients.

Echo-Server und einfacher Client (F3.1)

EchoRun installiert auf dem Netz von tcp-lan Filius' Echo-Server (ServerBaustein) auf .10, eingeschaltet auf Port 55555, bevor die Simulation startet (wie eine Datei ihn lädt), und den einfachen Client (ClientBaustein) auf .20 und .30. Es macht die Aufrufe wie die Fenster: verbinden und trennen gehen an den Thread des Programms, senden läuft im Thread des Aufrufers, bis es zurückkehrt. Es schreibt nach echo-java-<ablauf>.java.json die Frames jeder Karte, Javas Tabellen und jede Zeile, die ein Programm in sein Fenster schreibt (seine Beobachter), dazu die Aufrufe. Abläufe: exchange, closedPort, noRoute, notConnected, serverOff, reconnect, twoClients, otherPort.

Gemessen (01.10.2026):

  • Anwendung.starten meldet seinen Beobachtern null; das Fenster überspringt es.
  • Der Client merkt die Verbindung erst beim nächsten Blick (TCPSocket.verbinden schläft 100 ms), der Server ebenso eine neue Verbindung (ServerSocket.oeffnen) und dass er ausgeschaltet wurde („Server-Socket geschlossen“ rund 20 ms nach dem Ausschalten).
  • Ohne Route zum Ziel stirbt der Socket-Thread des Clients an der RouteNotFoundException; verbinden wartet ewig, das Fenster bleibt stumm, kein Frame geht hinaus.
  • Ausgeschaltet schließt der Server seine Verbindungen mit FIN, gibt aber zugleich den Port frei (ServerSocket.schliessen); das FIN des Clients findet keinen Socket mehr und bleibt ohne ACK.
  • Aus- und sofort wieder eingeschaltet (ohne Pause) merkt Javas Schleife das Ausschalten nicht und lauscht weiter auf dem alten Port, auch wenn er geändert wurde. Wer im Fenster den Port wechselt, lässt Sekunden dazwischen; so misst es otherPort.
  • Der Client sendet nur ein SYN (wie bei TcpRun, closedPort).

Webserver und Browser (F3.2a)

WebRun installiert auf dem Netz von web-lan (Browser 192.168.0.20, DNS-Server .10, Webserver .30, ein Rechner ohne Webserver .31) Filius' Webserver, eingeschaltet auf Port 80, mit den Dateien, die Filius ihm gibt (index.html und ihr Bild), und eigenen (Seite mit Bildern, Text, Namen mit Leerzeichen und Umlaut, virtuelle Hosts in /www.conf/vhosts, eine Datei außerhalb von /webserver). Eine Adresse wird eingetippt wie ins Fenster und wie dort zur URL (erzeugeURL). Es schreibt die Frames jeder Karte, Javas Tabellen, die Fensterzeilen des Servers, was der Browser seinem Fenster meldet (Seite mit Status, Inhaltsart und Text, oder „update“ für ein Bild), und alle Dateien des Servers am Ende. Abläufe: page, byName, notFound, vhost, noServer, unresolved, post, encoded, traversal, caseInsensitive, raw (der einfache Client spricht mit dem Server).

Gemessen (01.10.2026):

  • Der Browser verbindet sich immer mit Port 80; die Adresszeile nimmt keinen Port (Javas Muster für Hosts). Ein Schrägstrich am Ende fällt weg (String.split), /vhost1/ ist dann ein Ordner und 404.
  • Je Seite eine Verbindung: Seite, dann jedes <img> nacheinander (das rohe src, auch IMG SRC='…' und ohne Anführungszeichen, relativ ohne führenden Schrägstrich), dann schließen. Ein Bild kommt als Javas Base64 mit Zeilenumbruch nach 76 Zeichen.
  • Pfade gehen kodiert (UriUtils.encodePath: %20, %C3%BC), die Anfrage ohne Abfrage (getPfad), mit dem Host, wie er eingetippt wurde.
  • GET /../geheim.txt liefert /geheim.txt: Javas Dateisystem löst .. auf, der Server prüft nicht, ob es unter /webserver bleibt.
  • Gesucht wird ohne Rücksicht auf Groß- und Kleinschreibung (Name und Host).
  • „HALLO“ oder „GET /“ ohne Version: HTTPNachricht wirft, der Thread des Servers schließt die Verbindung und meldet null.
  • Beim Wechsel der Seite geht die DNS-Anfrage der neuen Seite vor dem FIN der alten: schliessen() bittet den Socket-Thread nur.
  • Die Fehlerseiten macht der Browser selbst (im Messaufbau Javas Ersatztext, weil die Vorlage fehlt); der Vergleich sieht nur den Status.

Seiten der Router und Heimrouter (F3.2b)

RouterWebRun lässt den Browser eines Rechners die Seiten eines Routers oder Heimrouters holen: auf router-web (arp-routed mit Firewall auf dem Router: ICMP verwerfen, TCP-Port 80 aus 10.0.0.0/24 erlauben) Index, routes, log (nach einem verworfenen Ping und zwei Verbindungen) und konfig; auf nat-home den Index des Heimrouters von innen und von außen und routes (gibt es dort nicht: 404); auf rip-chain nach 40 s die RIP-Tabelle (die Router wie bei RipRun gesetzt). ScenarioNetwork baut dafür jetzt auch Firewalls nach dem Szenario-Feld firewalls.

Gemessen (01.10.2026):

  • Router und Heimrouter schalten ihren Webserver beim Start ein (setAktiv(true), Fensterzeile „Annahme … gestartet“). Ob er lauscht, ist ein Wettlauf: setAktiv weckt den Thread nur, wenn er schon wartet. In einem von drei RIP-Läufen blieb das SYN an den Router unbeantwortet; in den NAT-Aufnahmen hat er nie gelauscht (er nahm keine Startnummer).
  • Index-Seiten aus Javas Vorlagen, zeilenweise gelesen und mit \n wieder zusammengesetzt (auch die Windows-Zeilenenden der deutschen).
  • log: ein Eintrag je Regeltreffer bei TCP und UDP und je verworfenem ICMP-Paket; die deutsche Seite schließt den Titel mit </titel>.
  • routes ohne RIP: Javas Weiterleitungstabelle (eigene Adressen, Netze, localhost, manuelle Routen, Standardgateway), grün, was direkt ist, blau das Standardgateway; mit RIP: Hops, Gültigkeit in ganzen Sekunden, nächster Hop und die erste Adresse des meldenden Routers.
  • Java zählt TCP-Sequenznummern und Segmente in Zeichen seiner Strings: Ein „ä“ auf der Routen-Seite ist eine Stelle, nicht zwei Bytes.

Firewall-Einstellungen der Router (F3.2c)

KonfigCases füttert Javas Seite konfig (FirewallWebKonfig) direkt mit den Schritten aus packages/core/test/firewall-settings.cases.txt (eine Anfrage ohne Daten oder die Daten eines Formulars) und schreibt je Schritt die Seite und die Firewall danach, auf Deutsch und auf Englisch (firewall-settings.{de,en}.java.json; das Gebietsschema wird vor dem ersten Lesen des Nachrichtenbündels gesetzt). Jeder Fall beginnt mit einem neuen Router, ohne Regeln, mit drei oder mit der einen Regel, die ein Fall anlegt (die Seite, die Java zeigen würde, hätte es sie angenommen). RouterWebRun spielt dazu auf router-web konfigIcmp und konfigBlock über das Netz.

Gemessen (01.10.2026):

  • Javas Browserfenster (Swing, FormSubmitEvent.getData) schickt jedes Feld des Formulars in seiner Reihenfolge, UTF-8-kodiert, angehakte Kästchen und die gewählte Option, dazu nur den gedrückten Knopf.
  • Java prüft die Felder beim Lesen: die Quell-IP gegen die Maske, die noch nicht gelesen ist (also leer), die Maske gegen die IP, ebenso. Eine Regel mit Quell- oder Zieladresse nimmt die Seite deshalb nie an, in keiner Reihenfolge der Felder.
  • Ein Fehler bleibt im Plug-in stehen und erscheint auf jeder späteren Seite, auch ohne Daten. Mit zwei Knöpfen zugleich (Speichern und Hinzufügen) speichert Java und schreibt die Fehler als nackte Liste.
  • Java dekodiert die Daten, bevor es sie an & und = zerlegt: Ein getipptes & zerreißt das Formular. Eine kaputte Kodierung (%zz, % am Ende) wirft im Dekodierer, Regel 0 nach unten wirft (ArrayIndexOutOfBoundsException), ein $ im wieder gefüllten Formular lässt replaceAll scheitern (die Seite ist null, also 500).
  • Die Maske prüft isValidSubnetmask: vier Zahlen (eine leere am Ende fällt bei Javas split weg), parseInt mit Vorzeichen, die Bits Einsen, dann Nullen, mindestens eine Eins (0.0.0.0 ist keine Maske).
  • Über das Netz: „ICMP filtern“ aus, und der nächste Ping kommt durch; eine Regel „TCP 80 verwerfen“ über die erlaubende geschoben, und das SYN des Browsers bleibt an der Firewall hängen. Javas Router-Server lauschte auch hier in einem Lauf nie (run.sh wiederholt).

E-Mail-Server (F3.3a)

EmailRun lässt Javas einfachen Client (ClientBaustein) auf mail-lan rohe SMTP- und POP3-Befehle an einen E-Mail-Server schicken (Domain filius.de, Konten aus /mailserver/konten.txt: anna mit zwei Mails, bob ohne). Aufgezeichnet werden die Frames, die Fensterzeilen von Server und Client und am Ende die Konten (jede Mail als Email.toString, neu, gelöscht). MailCases füttert Javas SMTPMitarbeiter und POP3Mitarbeiter ohne Netz über einen Socket, der nur mitschreibt, mit den Fällen aus packages/core/test/email-server.cases.txt, auf Deutsch und auf Englisch. Die Netz-Abläufe bleiben bei dem, was Java richtig macht; Javas Fehler zeigen die Fälle.

Gemessen (01.10.2026):

  • Der Server startet POP3 (Port 110) vor SMTP (Port 25); POP3s Socket nimmt die erste TCP-Startnummer. Die SMTP-Antworten sind übersetzt („220 Willkommen bei“), die POP3-Antworten englisch. SMTP schreibt seine Fensterzeile nach dem Senden, POP3 davor.

  • Eine angenommene Mail speichert Java so, wie es ihren Text neu liest (new Email(email.toString())): Zeilenenden ohne \r, kein Bcc.

  • SMTP-Fehler: Nach einer Mail beantwortet der Server den nächsten Befehl mit 503 und behält Absender, Empfänger und Text (die zweite Mail ging auch an die erste Empfängerin, mit dem ersten Text); unbekannte oder verfrühte Befehle bleiben ohne Antwort; ein quit im Mailtext beendet die Verbindung; die Domain wird genau verglichen (FILIUS.DE ist fremd); ein leerer Absender mit unbekanntem Empfänger und ein Empfänger ohne @ werfen im Mitarbeiter, die Verbindung endet; eine unbekannte Kopfzeile beendet den Kopf (der Betreff wird Text), ohne Leerzeile fehlt die erste Zeile; eine Benachrichtigung an eine Mail ohne From hat To: null.

  • Die Benachrichtigung über unbekannte Empfänger trägt Javas Datum im Gebietsschema der JVM (hier 10/1/26, 6:30 PM) und die Adresse des Servers.

  • POP3 zählt Mails ab 0. Fehler: eine Mail lässt sich nur einmal abrufen; LIST ohne Mails gibt -ERR, LIST 0 listet alle; vor der Anmeldung kommen leere Antworten; eine fehlende Mail kommt als leere +OK-Mail, DELE darauf leer; ein fehlendes oder falsches Argument wirft, die Verbindung endet; nach DELE nummeriert LIST neu, RETR nicht.

  • Die Größe in STAT und LIST ist Javas eigene: die Länge von Text, Absender, Betreff, Empfängerliste ([a, b]) und Datum.

  • Die Kontendatei /mailserver/konten.txt (seit F3.5a mitgeschrieben, wie Java sie hinterlässt und wie kontenSpeichern die Konten am Ende schreibt, 02.10.2026): Java schreibt sie nach einer angenommenen Mail, nach POP3-Befehlen, bei Kontoänderungen und beim Stoppen; die Benachrichtigung an einen lokalen Absender schickt der SMTP-Arbeiter erst nach dem Speichern, sie fehlt in der Datei bis zum nächsten Mal. Felder ohne Wert (das Datum) schreibt Java leer.

E-Mail-Programm (F3.3b)

EmailClientRun lässt Javas E-Mail-Programm (EmailAnwendung) auf mail-lan mit zwei Servern arbeiten: filius.de auf .30 (die Konten von EmailRun), andere.de auf .31 (carla); der DNS-Server kennt die MX beider Domains, und beide Server fragen ihn. Das Konto ist annas; eine Mail entsteht wie im Fenster (Absender „Anna Muster anna@filius.de“, die Adressen der Felder) und geht an den SMTP-Server des Kontos, Abholen fragt den POP3-Server. Aufgezeichnet werden die Frames, was Programm und Server ihren Fenstern sagen, und am Ende Posteingang, Gesendete und die Konten beider Server. Alle Mail-Orakel laufen seitdem in einer JVM mit dem Gebietsschema der Sprache (-Duser.language=de -Duser.country=DE, für die englischen Fälle en_GB): die Benachrichtigung trägt das Datum aus DateFormat, dessen Länge jede spätere Sequenznummer verschiebt.

Gemessen (01.10.2026):

  • Das Programm baut für jeden Empfänger (An, Cc, Bcc) eine eigene Verbindung zum SMTP-Server des Kontos auf, immer an Port 25 (der SMTP-Port des Kontos wird nicht gelesen). Ein Name als Server wird bei jeder Verbindung neu aufgelöst; die DNS-Anfrage geht noch vor dem FIN der letzten Verbindung, das SYN der neuen danach.
  • Der Server schickt Mails an fremde Domains in seinem eigenen Thread weiter: MX nachschlagen, dann ein neuer SMTP-Client je Weiterleitung. Ohne MX bekommt der Absender der eigenen Domain eine Benachrichtigung; ist der Absender fremd, schickt der Server sie mit leerem Absender (MAIL FROM: <>) an dessen Domain, nachgeschlagen im Mitarbeiter-Thread, der dafür wartet.
  • Abholen: USER, PASS, STAT, LIST, dann je Mail RETR und DELE, QUIT. Eine Antwort, die nicht passt, kommt als Ausnahme ins Fenster (java.lang.Exception: -ERR user or password wrong), ein Verbindungsfehler ebenso (TimeOutException).
  • Ohne Server schickt der Client nur ein SYN (bekannter Java-Fehler aus TcpRun); ein unbekannter Servername kommt als VerbindungsException: Ziel konnte nicht bestimmt werden!.
  • Wer von zwei fast gleichzeitig gebauten Sockets die nächste TCP-Startnummer zieht, entscheidet der Thread-Takt (Programm und Weiterleitung des Servers, wenige Millisekunden auseinander).

Befehlszeile, lokale Befehle (F3.4a)

TerminalCases füttert Javas Terminal mit Zeilen, wie sie getippt werden, und zerlegt sie wie das Fenster (GUIApplicationTerminalWindow: erstes Wort der Befehl, dann die Parameter, mindestens drei leere dazu; exit, cls, reset und leere Zeilen behandelt das Fenster selbst). Ohne laufendes Netz, auf Rechner 10.0.0.2 von router-web mit festen Dateien. Geschrieben werden je Zeile, was das Programm dem Fenster sagt, der Arbeitsordner und alle Dateien, dazu die Terminal-Texte des Bündels, wie Java sie lädt (daraus terminalTexts.ts).

Gemessen (01.10.2026):

  • Java ruft jede Methode des Terminals mit String[]-Parameter auf, deren Name der Befehl ist, mit Groß- und Kleinschreibung (LS ist unbekannt).
  • Ordner listet ls in der Reihenfolge, in der die Einträge entstanden; Pfade findet Java ohne Rücksicht auf Groß- und Kleinschreibung und zeigt die gespeicherte Schreibweise.
  • Die Größe einer Datei: Java dekodiert jeden Inhalt als Base64 und zählt die Bytes, nur bei einem Fehler die Zeichen; „Hallo Welt“ hat 6.
  • Fehler: ls auf einen fehlenden Ordner sagt „leer“, dir / schreibt „Verzeichnis :“; cd auf eine Datei macht sie zum Arbeitsordner; touch und mkdir in einem fehlenden Ordner melden „erstellt“ bzw. „bereits vorhanden“; cp auf einen Ordnernamen wirft; .. über der Wurzel findet nichts oder wirft; rm (und das Löschen bei mv) hängt einen absoluten Pfad an den Arbeitsordner, rm / löscht den Arbeitsordner und meldet Erfolg; mv auf die Datei selbst (auch in anderer Schreibweise) löscht sie.

Befehlszeile, Netzbefehle (F3.4b)

TerminalRun tippt Zeilen in Javas Terminal auf den Rechnern von terminal-net (LAN 192.168.0.0/24 mit Client .20, DNS-Server .10 ohne Gateway, .30, .31, Router .1/10.0.0.1, dahinter 10.0.0.2 mit einem DNS-Server, den es nicht gibt), zu festen Zeiten und so, wie das Fenster sie übergibt (terminalEingabeAuswerten); ^C ist Strg+C (setInterrupt). Geschrieben werden je Karte die Frames samt Javas Tabellen, je Rechner was die Befehlszeile sagt (Texte und Laufmarken) mit der Zeit, und die getippten Zeilen mit ihren Zeiten, die der Test des Kerns ebenso tippt. Die JVM läuft deutsch.

Gemessen (02.10.2026):

  • ping: vier Echos, je eines pro Sekunde ab dem Senden des vorigen; die Zeit zählt ab vor dem ARP (473 ms im LAN mit ARP, dann 223 ms; über den Router 673 und 334 ms, TTL 63). Gewartet wird nach dem Senden eine RTT (hier 2750 ms) auf die erste ICMP-Nachricht, gleich welche; alles außer der Antwort mit der Sequenznummer ist „Timeout!“, und das nächste Echo geht sofort. Ohne ARP-Antwort legt sich der Rechner selbst „Host Unreachable“ hin (je Echo zwei ARP-Anfragen, 5,5 s); hinter dem Router fragt dieser seine ARP-Anfragen nacheinander ab, so beantwortet das dritte Echo die Meldung zum ersten. Ohne Route am Router kommt „Network Unreachable“ sofort, viermal in 900 ms. Strg+C wirkt vor dem nächsten Echo, die Statistik zählt die gesendeten; ein danach getippter bekannter Befehl hebt es auf (setInterrupt(false) beim Übergeben), ein unbekannter nicht. Einen unbekannten Befehl meldet das Fenster sofort, auch während ein anderer läuft.
  • Broadcast: ohne -b „nicht unterstützt“ (auch 255.255.255.255 über die Standardroute, Maske 0.0.0.0); mit -b geht das Echo mit TTL 1 an FF:FF:FF:FF:FF:FF, alle im Netz antworten (auch der Router), jeder fragt dazu per ARP nach dem Absender. Welche Antwort zuerst kommt, ist ein Wettlauf der Threads. Ein gerichteter Broadcast aus einem anderen Netz (10.0.0.2 an 192.168.0.255) wird am Router verworfen.
  • Eigene Adresse: Javas Route geht über 127.0.0.1, die Antwort kommt „From 127.0.0.1“; ebenso für localhost und 127.0.0.1, ohne Frame.
  • Ferne Adresse eines Routers: die Antwort kommt von der nahen Karte („From 192.168.0.1“ für ping 10.0.0.1).
  • Fehler Javas: in der Statistik und bei „Timeout!“ steht der erste Parameter statt des Hosts (--- -b Paketstatistik); jedes erste Wort gilt als -b (ping -x 192.168.0.31 pingt). „Network Unreachable“ vom Router bettet ein halb gebautes Paket ein (sender=null, TTL 63).
  • host: A, dann MX, Namen mit Punkt am Ende; „nicht aufgelöst“ erst nach beiden Anfragen (auch MX allein zählt); localhost ohne Anfrage. Ohne erreichbaren DNS-Server nach fünf RTT „Zeitüberschreitung“, bei ping mit „ (DNS)“ und ohne Laufmarken. Ohne eingetragenen DNS-Server sofort „nicht aufgelöst“.
  • nslookup: Server und Adresse (127.0.0.1 für localhost), dann nur der Antwortteil; ohne Treffer nur der Kopf. Ohne Parameter die Hilfe ohne Laufmarken (host mit).
  • arp: Javas Tabelle ist eine HashMap mit der Zeile 255.255.255.255, die jedes Zurücksetzen (-d) neu anlegt; -a zeigt eine Zeile, -d mit Adresse löscht sie und zeigt dann die Tabelle; unbekannte Optionen und -a/-d mit etwas anderem als einer Adresse zeigen die Tabelle. arp -d 255.255.255.255 entfernt auch die Broadcast-Zeile, bis arp -d.
  • netstat: TCP, dann UDP, je Socket die lokale Adresse nach der Route zur Gegenstelle; ein Server mit 0.0.0.0 und -:- in LISTEN, darunter seine Verbindungen; eine getrennte Verbindung steht beim Client in TIME_WAIT, beim Server ist sie ausgetragen. Javas UDP-Server behält für jede je beantwortete Adresse und jeden Port einen Socket (CONNECTED), dessen Arbeiter für immer wartet. Die Reihenfolge ist die einer HashMap bzw. Hashtable.
  • Javas waitForIcmpResponse verpasst sein Aufwecken, wenn die Antwort schon da ist, bevor es wartet (ein Wettlauf der Threads: Echo an sich selbst, das „Host Unreachable“, das sich ein Rechner selbst hinlegt), und wartet dann eine volle RTT. Eine Aufnahme damit (ein Ping an sich selbst über 1 s, mehr als 6 s zwischen zwei Timeouts ins Leere) wird wiederholt; ebenso eine unter Last (andere Sitzungen auf dem Rechner: die erste ARP-Runde dauert dann knapp 300 statt 225 ms).

Gnutella, Beitreten (F3.6a)

GnutellaRun installiert Javas Gnutella-Programm (PeerToPeerAnwendung) auf vier Rechnern von terminal-net (192.168.0.20, .30, .31 und 10.0.0.2 hinter dem Router; .10 ohne) und ruft beitretenNetzwerk und resetNetwork zu festen Zeiten, wie das Fenster es tut. Geschrieben werden je Karte die Frames samt Tabellen, die bekannten Teilnehmer jedes Programms bei jeder Meldung an sein Fenster und am Ende.

Gemessen (02.10.2026):

  • Ein Paket ist Text, seine Felder durch // getrennt (PeerToPeerPaket .toString): Ping guid//0x00//hops//ttl//0//IP, Pong guid//0x01//0//8//Bits//IP//6346//Dateien//Bytes; die Bitlänge zählt die Zeichen der Adresse mal acht und für jede Zahl die kleinste Zweierpotenz, die sie fasst. Die GUID ist zufällig (new Random()).
  • Beitreten: eine Verbindung zum Port 6346 des Teilnehmers, der Ping; der Teilnehmer nimmt den Absender des Pings auf, antwortet mit einem Pong und schickt den Ping (hops+1, ttl−1) über je eine neue Verbindung an seine anderen Teilnehmer, außer an den Absender und den Rechner des Pings; deren Pongs gehen (hops+1, ttl−1) über die Verbindung zurück, auf der der Ping kam (sendePaket nach GUID). Wer einen Pong auf seinen eigenen Ping bekommt, nimmt dessen Rechner auf; jeder höchstens so viele, wie er behält. Keine Verbindung wird je geschlossen.
  • Fehler Javas: kommt derselbe Ping ein zweites Mal an, gibt erstellePong null zurück, und toString() darauf wirft; der Arbeiter schließt die Verbindung (gemeint war: kein Pong). Ein Text ohne Paketkopf wirft ebenso und schließt die Verbindung.
  • Zu sich selbst kein Ping; zu einem Rechner ohne Programm ein SYN (Javas eines statt drei); ins Leere nur ARP.
  • Wo viele Verbindungen zugleich laufen, kommen die Frames zweier Verbindungen (oder beider Richtungen einer) an einer Karte mal so, mal so herum, einige zehn Millisekunden auseinander: Javas Thread-Takt über mehrere Sprünge.

Gnutella, Suche und Download (F3.6b)

Zwei weitere Abläufe von GnutellaRun: die Teilnehmer bilden über ihre Höchstzahlen eine Linie .20 – .30 – .31 – 10.0.0.2; .31 teilt notizen.txt (in searchBoth auch lied.txt), 10.0.0.2 Lied der Woche.txt. Geschrieben werden zusätzlich die Suchergebnisse bei jeder Meldung und am Ende die Dateien in /peer2peer jedes Programms.

Gemessen (02.10.2026):

  • Suche (sucheDatei): die Ergebnisse geleert, eine Query guid//0x80//0//8//Bits//1//Worte (ein leeres Feld als ein Leerzeichen) über je eine neue Verbindung an jeden Teilnehmer außer sich selbst. Wer sie noch nicht kennt, schickt sie (hops+1, ttl−1) an seine Teilnehmer außer dem Absender und antwortet für jede Datei in /peer2peer, deren Name die Worte enthält (Groß/klein egal), mit einem Treffer guid//0x81//0//8//Bits//Anzahl//6346//IP//2//Name: Größe B// ; die Bitlänge rechnet Java, bevor der Name eingetragen ist. Treffer gehen (hops+1, ttl−1) zurück wie Pongs; das Ergebnis heißt IP/Name: Größe B, jedes einmal.
  • Fehler Javas: wer selbst Treffer schickt, schließt danach die Verbindung; die Treffer der Teilnehmer, an die er die Query weitergeschickt hat, gehen verloren (searchBoth: der von 10.0.0.2). Die Größe ist wieder die des Base64-dekodierten Textes („Hallo Welt“ = 6 B), ebenso die Bytes im Pong.
  • Download (herunterladenDatei): GET Name HTTP/1.1 mit Host: an Port 6346 des Rechners (der Name bis zum :, Leerzeichen kodiert), Antwort HTTP/1.1 200 OK mit Content-type: gleich dem Dateityp und dem Inhalt, sonst HTTP/1.1 404 Not Found; die Datei landet in /peer2peer, die Verbindung schließt der Client nach der Antwort.

Datei-Explorer, Texteditor, Bildbetrachter (F3.5b)

FileCases tut, was die Fenster dieser Programme mit den Dateien eines Rechners tun (GUIApplicationFileExplorerWindow, GUIApplicationTextEditorWindow; der Bildbetrachter liest nur), Schritt für Schritt aus file-programs.cases.txt, ohne laufendes Netz, ab den Dateien von TerminalCases, in beiden Sprachen. Geschrieben werden je Schritt die Rückgabe (Meldung, Ausnahme) und alle Dateien, dazu die Meldungen des Explorers.

Gemessen (02.10.2026):

  • Neuer Ordner (erstelleVerzeichnis): der Name ist ein Pfad (a/b legt b in a an, /x unter der Wurzel, ../x darüber); ein vorhandener Name ändert nichts; .. an der Wurzel legt einen Ordner ohne Namen an.
  • Einfügen: eine tiefe Kopie ans Ende des Ordners; ein zweites Einfügen desselben Namens legt eine zweite Datei gleichen Namens an; nicht in das Kopierte selbst oder darunter (fileexplorer_msg19); Ausschneiden und in denselben Ordner einfügen schiebt den Eintrag ans Ende.
  • Umbenennen: der Eintrag behält seine Stelle; ein leerer oder vorhandener Name ändert nichts, auch eine reine Änderung der Groß-/Kleinschreibung (Java findet den Eintrag selbst); a/b wird eine Datei mit / im Namen; Leerzeichen am Rand bleiben.
  • Import (FileExplorer.addFile): der Typ nach dem Namen der lokalen Datei (config/filetypes.txt; ohne Endung Text, unbekannte Endung kein Typ); Text Zeile für Zeile, jede mit CR LF (auch die letzte), UTF-8; anderes als Base64; über 150 000 Byte abgelehnt; ohne Namen noName; auf einen vorhandenen Namen Inhalt und Typ ersetzt, Schreibweise bleibt; auf einen Ordnernamen eine Ausnahme.
  • Texteditor: „Speichern“ ersetzt den Inhalt der offenen Datei, „Speichern unter“ (speicherDatei) legt eine Datei vom Typ text an oder ersetzt Inhalt und Typ einer gleichnamigen (auch eines Bildes); auf einen Ordnernamen eine Ausnahme, in einem fehlenden Ordner nichts.

Befehlszeile, traceroute und tcpdump (F3.4c)

Weitere Abläufe von TerminalRun auf terminal-net. Gemessen (02.10.2026):

  • traceroute: je Sprung bis zu drei Proben (Echo mit TTL), je eine RTT auf die erste ICMP-Nachricht; eine Antwort zählt, wenn sie die Probe trägt (Echo-Antwort mit ihrer Nummer, oder ein Fehler mit 0/0 und der Probe darin), sonst „* “. Die Fehlzahl wird nur bei einem Treffer zurückgesetzt: nach mehr als fünf Fehlschlägen bricht Java ab. Die Nummern zählen ab 42 · 23, der Identifier ist der von ping.
  • Das Fazit kommt nach der Laufmarke „fertig“, das Fenster zeigt es dann nicht mehr (GUIApplicationTerminalWindow.update zeigt Text nur, solange ein Befehl läuft). Erreicht das Ziel den 20. Sprung, nennt Java es „nicht erreicht“ (ttl >= maxHops, aus dem Code; gemessen bis zwei Sprünge).
  • Eigene Adresse, ferne Router-Adresse, das „Host Unreachable“, das sich ein Rechner selbst gibt: Java nennt 127.0.0.1 bzw. die nahe Karte.
  • Hinter dem Router ohne Empfänger: Der Router hält während seiner ARP-Suche die Sperre seines ICMP-Puffers (ProtokollThread.run verarbeitet in synchronized); der Ethernet-Thread seiner Karte blockiert beim Übergeben, und Rahmen dahinter liest und protokolliert die Karte erst danach (die sechste Probe 2,5 s zu spät).
  • tcpdump: je Rahmen der ersten Karte ab dem Start die oberste Zeile der Tabelle (Uhrzeit, Protokoll, Quelle > Ziel, Kommentar), alle 100 ms; Zeilenumbrüche werden zu zwei Leerzeichen; Strg+C beendet es nach dem nächsten Blick. Ein während dessen getippter Befehl wartet bis danach.

Unterrichtsklassiker (Abnahme F3)

ExampleRun hat drei weitere Abläufe auf Beispielen von Filius. Was jedes Programm seinem Fenster meldet, steht unter programs (Zeit, Programm, Text; ein Aufruf des Ablaufs als call …), wo die Programme enden unter end:

  • web (internetwork_komplett): Notebook 1.10 tippt www.filius.de in seinen Browser. Der Name wird bei DNS-Server 2.10 im anderen Netz erfragt, die Seite samt Bild kommt von Webserver 5.10 zwei Router weiter.
  • email (Internet_Komplett_…_DE): Wenn die DHCP-Clients ihre Adressen haben (8 s), schreibt herbert@gmx.de (das erste Notebook, im Schulnetz hinter der Firewall) an frimp@webmail.de. mail.gmx.de reicht die Mail an mail.webmail.de im anderen Netz weiter, bei 22 s holt frimp (Notebook#2) ab.
  • gnutella (zwei_rechnernetze_komplett): Filius' Gnutella-Programm auf Notebook 1.10, Notebook 1.11 und DNS-Server 2.10 (der „Lied der Woche.txt“ teilt), je höchstens drei Teilnehmer. 1.10 tritt bei 2.10 bei, dann 1.11 bei 1.10: Der Ping geht über 1.10 weiter zu 2.10, der Pong auf demselben Weg zurück. 1.10 sucht „woche“ (2.10 reicht die Query an 1.11 weiter) und holt den Treffer über den Router.

Aufnahmelängen 12 s (web), 40 s (email), 26 s (gnutella): Das Ende liegt jeweils, wo nichts mehr geschieht. Bei 34 s fiel Javas letzter Abbau einer Verbindung knapp hinter das Ende, bei 14 s ein wiederholtes SYN.

Gemessen (03.10.2026):

  • Die Datei hält die Konten der E-Mail-Programme nur in der älteren kontoListe, die Filius 2.14 nicht mehr liest: Nach dem Laden hat Java kein Konto. Der Leser übernimmt sie (importEmail.ts). Das Orakel trägt sie so ein, wie man sie im Kontodialog eintippt, mit den Werten der Liste.
  • Notebook 1.11 in zwei_rechnernetze hat kein Gateway (mit Absicht im Beispiel). Lernt es über einen Pong einen Teilnehmer im anderen Netz, bleibt in Java der Thread jeder Verbindung dorthin in TCPSocket.verbinden hängen: kein SYN auf der Leitung, keine Meldung, ein Download kommt nie an. Der Ablauf lässt darum 1.10 suchen.
  • Jeder TCP-Socket zieht beim Anlegen die nächste Anfangsnummer (ein gemeinsamer Zähler). Ein Server legt seinen ersten lauschenden Socket beim Start in seinem eigenen Thread an (ServerAnwendung.annehmenVerbindungen): Welcher Server eines Beispiels welche Nummer zieht, entscheidet der Takt der Threads. Verglichen wird unterhalb der Millionen.
  • Eine Suche geht zugleich an beide Teilnehmer von 1.10. An seiner Karte kommen die Frames der zwei Verbindungen in beliebiger Folge, einige zehn Millisekunden auseinander (in einer Aufnahme vertauscht, in der nächsten nicht).
  • 2.10 reicht die Query an 1.11 weiter, das ohne Gateway nicht antworten kann: Java schickt das SYN einmal und gibt nach einer Wartezeit auf (wie in closedPort von TcpRun); 1.11 seinerseits erreicht 2.10 gar nicht (s. o., kein Frame).
  • Gleich lange RIP-Wege, gemessen: Router #3 erreicht das Schulnetz (42.0.0.0) über Router "" oder über #2, je zwei Sprünge. Jede Antwort an die Schule läuft dort durch, die des DNS 8.8.8.8 wie die von webmail. In beiden ruhigen Aufnahmen nahm Java #2, der Kern "" (die zuerst gehörte Route); über #2 kosten zwei neue ARP-Suchen rund 270 ms. Der Test gibt dem Kern Javas Wahl, wie eine von Java gezogene Zufallszahl, und nur bei gleicher Länge.
  • RIP-Beacons kommen alle 30 s nach Zufallspausen, so viele, wie ein Router seit dem Start gezogen hat (s. RIP oben); verglichen werden in den Klassikern die Frames der Programme, RIP nicht.
  • Java fällt an jedem Verbindungsaufbau um einen Blick (rund 80 ms) zurück, dazwischen um wenige Millisekunden je Sekunde: In der E-Mail-Geschichte folgt die Weitergabe gmx → webmail auf herberts Verbindung, ihre Frames liegen bis zu 272 ms hinter dem Kern.

Neue Geräte aus der Palette (F4.4a)

NewDeviceRun legt jede Geräteart so an wie Filius' Palette (GUIContainer.neuerKnoten: Konstruktor, beim Vermittlungsrechner die Schnittstellenzahl seines Dialogs, Beschriftung, setProperty, das die Einstellungen des Geräts öffnet) und speichert sie wie Filius (SzenarioVerwaltung.speichern) nach packages/doc/test/fixtures/new-devices.fls: Rechner, Notebook, Switch, Vermittlungsrechner mit 2 bis 8 Schnittstellen, Heimrouter, Modem.

Gemessen (03.10.2026):

  • Rechner und Notebook: eine Karte 192.168.0.10/255.255.255.0, kein Gateway, kein DNS, leeres Dateisystem, keine Programme.
  • Switch: 24 Ports, SSID gewürfelt (sechs Hexziffern).
  • Vermittlungsrechner: so viele Karten wie gewählt, alle 192.168.0.10/24, Firewall aus (Standardregeln), kein Eintrag als Gerät; schließt man den Dialog, sind es 2.
  • Heimrouter: WAN 42.0.0.10/255.0.0.0, LAN 192.168.0.10/24, Firewall aus.
  • Modem: ein Port, Port 12345.
  • MAC-Adressen gewürfelt. Der Name ist der Typ der Geräteart in der Sprache der Oberfläche (Knoten.TYPE).
  • Ablegen: Die Vorschau hängt mittig am Zeiger; nach dem Setzen der Beschriftung rückt Java den Knopf so, dass das Symbol bleibt, wo die Vorschau war (initTextAndUpdateLocation).
  • Kabel (GUIEvents): der erste freie Port; am Heimrouter WAN, wenn man die linke Hälfte des Knopfs trifft, sonst LAN; ein Funkgerät nur an einen Switch; zwei Geräte nur einmal verbunden. Javas Meldungen: guievents_msg1 (kein freier Port), msg22/msg23 (WAN/LAN belegt), msg26 (drahtlos nicht möglich), msg12 (bereits verbunden).
  • Ein Wettlauf im Swing-Thread wirft beim Speichern gelegentlich eine Ausnahme; die Datei wird trotzdem vollständig geschrieben.

Das Geräte-Blatt (F4.5a)

SheetRun tippt die Texte aus packages/doc/test/sheet.cases.txt in Filius' eigene Einstellungsfelder (JHostKonfiguration, JSwitchKonfiguration, JModemKonfiguration, per Reflection), ruft wie beim Verlassen eines Felds aenderungenAnnehmen und hält je Fall fest, ob das Feld beim Tippen rot war, was das Gerät danach hält und was das Feld dann zeigt (packages/doc/test/sheet.java.json). Jeder Fall beginnt mit einem neuen Gerät; ein Rechner hat Gateway 192.168.0.254 und DNS 192.168.0.253, ein Switch die SSID „Start“, damit sichtbar wird, was ein Fall stehen lässt. Dazu drei Abläufe der Kästchen.

Gemessen (03.10.2026):

  • Markieren (beim Tippen: das Muster auf den Text) und Übernehmen (beim Verlassen: IP.ipCheck, dann das Muster auf dessen Ergebnis) sind zwei Urteile, die sich widersprechen. Rot, aber übernommen: „10..0.0.1“, „+10.0.0.1“, „10.0.0.1.“, „١٠.0.0.1“, „-0.0.0.1“ (als 0.0.0.1), „255..255.0.0“, „192.168..1.1“, Haltezeit „-5“ (−5000 ms) und „٣“. Schwarz, aber verworfen: „.0.0.1“, Haltezeit „“ und „99999999999999999999“.
  • Gateway und DNS: Nur Leerzeichen gelten als leer (rot, aber übernommen); ein ungültiger Text lässt den alten Wert stehen.
  • Ein verworfener Text verschwindet ohne Meldung: Das Feld zeigt danach, was das Gerät hält.
  • Name: jeder Text, auch leer und mit Leerzeichen. SSID: druckbares ASCII, mindestens ein Zeichen, Leerzeichen eingeschlossen.
  • Modem: Der Port wird nie markiert und jede int übernommen (0, 65536, −1, „080“ als 80); nur Text, der keine Zahl ist, bleibt ohne Wirkung.
  • „MAC-Adresse als Name verwenden“, dann ein anderes Feld übernommen: Der Name wird zur MAC-Adresse und bleibt es nach dem Abschalten (bei der IP-Adresse nicht).
  • „DHCP zur Konfiguration verwenden“ schaltet den eigenen DHCP-Server ab.

Vermittlungsrechner und Heimrouter (F4.5b)

SheetRun tippt seitdem auch in JVermittlungsrechnerKonfiguration (erste Karte, Gateway) und JGatewayConfiguration (Gateway, WAN, LAN). Felder ohne eigene Prüfmethode markiert es mit dem Muster ihres Tastenhörers. Dazu schreibt es Javas Weiterleitungstabellen für fünf Aufbauten (tables: holeTabelle mit den Kennzeichen der manuellen Zeilen) und Schritte in der Tabelle des Panels (edits: JWeiterleitungsTabelle, nur manuelle Zeilen gezeigt). Ein Panel, das wirft, gehört zur Messung (error).

Gemessen (03.10.2026):

  • Karte eines Routers: Markieren und Übernehmen stimmen überein (anders als beim Rechner wird „10..0.0.1“ verworfen), bis auf „.0.0.1“: nicht markiert, die Adresse wird null, und das Panel wirft danach eine NullPointerException (holeTabelle). Beim Heimrouter wird „.1.2.3“ ebenso null, ohne Ausnahme.
  • Gateway von Router und Heimrouter: Der Tastenhörer prüft mit musterIpAdresse, ein leeres Feld ist also rot, wird aber übernommen.
  • DHCP am Heimrouter (WAN) lässt den eigenen DHCP-Server laufen.
  • Tabelle: eigene Adressen (neueste Karte zuerst), eigene Netze (ebenso), localhost, die manuellen Zeilen (durch ipCheck: „010.020.0.0“ wird 10.20.0.0), das Standard-Gateway über die letzte Karte in seinem Netz, ohne solche über die erste.
  • Tabelle bearbeiten: Eine Zelle, die das Muster nicht besteht („abc“, „“, „10..0.0“), wird als null gespeichert; eine neue Zeile erst, wenn alle vier Zellen gültig sind; „Neuer Eintrag“ zweimal ergibt eine offene Zeile.

DHCP-Server und Portweiterleitung (F4.5c)

SheetRun tippt auch in JDHCPKonfiguration (eines Rechners mit Gateway und DNS oder eines Heimrouters; der Server mit Grenzen 100–200 und eigenem Gateway 10.0.0.1, DNS 10.0.0.2) und übernimmt mit dem Knopf (speichern). Dazu, was der Dialog ohne eigene Einstellungen zeigt, feste Zuordnungen aus seiner Tabelle (dhcp) und Zeilen des Portweiterleitungs-Dialogs (forwarding).

Gemessen (03.10.2026):

  • Grenzen, Gateway, DNS: Muster musterIpAdresse, also nie leer. Der Server speichert den Text wie getippt: „010.000.000.001“ bleibt so, „.0.0.1“ wird unmarkiert gespeichert.
  • Ohne eigene Einstellungen zeigt der Dialog Gateway und DNS des Rechners (gesperrt), ohne solche 0.0.0.0. Beim Heimrouter ist das Gateway immer seine LAN-Adresse (gesperrt), der DNS-Server immer frei; das Kästchen „Manuelle Einstellungen“ bewirkt dort nichts. Die Netzmaske ist die der ersten Karte (beim Heimrouter LAN), gesperrt.
  • Feste Zuordnungen: Javas Hinzufügen-Knopf prüft MAC (musterMacAddress, dessen Klasse [0-9a-fA-f] auch „_“ durchlässt) und Adresse; beim Speichern verwirft der Server eine zweite Zuordnung derselben MAC (ohne Rücksicht auf Groß-/Kleinschreibung) und ungültige Zeilen still. Die MAC bleibt, wie getippt.
  • Portweiterleitung: Der Dialog speichert jede Zeile roh, auch Port „0“, „abc“, „05353“; die Firmware leitet nur gültige weiter (validateData: musterProtocol, musterPort, musterIpAdresse).

Firewall (F4.5d)

SheetRun bedient auch JFirewallDialog eines Vermittlungsrechners (firewall): eine neue Regel, Zellen der Regeltabelle über Swings eigenes Bearbeitungsende (stopCellEditing → editingStopped → closeEditor), Verschieben und Löschen mit den Nummern, die Javas Knöpfe übergeben.

Gemessen (03.10.2026):

  • Neue Firewall: aus, Standardaktion verwerfen, ICMP nicht filtern, nur SYN verwerfen an. Neue Regel: alle Felder leer, TCP, alle Ports (−1), verwerfen.
  • closeEditor schreibt die Zelle in das Regelobjekt der Firewall, bevor validateExistingAttributes prüft; besteht die Regel nicht, wird nur die Tabelle nicht aufgefrischt. Die Firewall hält dann „abc“ als Quelle, „255.0.255.0“ oder „0.0.0.0“ als Maske, „999.999.999.999“ als Ziel, Port 0 oder 65536.
  • Masken: isValidSubnetmask (Einsen, dann Nullen, mindestens eine Eins). Quelle und Ziel: musterIpAdresse ohne ipCheck („.0.0.1“ und „010.0.0.0“ bleiben, wie getippt); die Marke „999.999.999.999“ gilt nur als Quelle. Leerzeichen gelten als leer.
  • Port: Integer.valueOf, was keine Zahl ist („abc“, „“), wird zu „alle Ports“; „080“ wird 80.
  • rauf/runter: Die erste Regel geht nicht höher, die letzte nicht tiefer (moveUp/moveDown antworten false).

Verbindungen verwalten (F4.5e)

SheetRun bedient auch JConnectionsDialog eines Vermittlungsrechners (connections): drei Karten (10.0.0.1, 10.1.0.1, 10.2.0.1), Rechner A an der ersten, Switch S an der zweiten, beide Kabel wie GUIEvents (createConnection) sie in den GUIContainer legt; dann Klicks auf die Knöpfe der Karten, „+“ und „-“, nach jedem Klick, was der Dialog zeigt (Beschriftungen, Knopffarben, ob „+“/„-“ frei sind) und woran die Karten hängen; bei einer und keiner Karte, ob das Router-Panel noch zeichnet und ob der Router ein Gateway behält.

Gemessen (03.10.2026):

  • Ein Klick markiert eine Karte gelb, ein zweiter auf dieselbe hebt das auf, ein Klick auf eine andere tauscht die Kabel (beide belegt) oder steckt das eine um (eine frei, auch eine markierte freie Karte); zwei freie: nichts. Danach ist keine Karte markiert, ebenso nach „+“ und „-“.
  • Beschriftungen: lokal „NIC n: Adresse“, Gegenstelle „Name (Adresse)“ bei einem Rechner oder Router (die Adresse seiner Karte am Kabel), beim Switch nur der Name.
  • „+“: eine Karte mit 192.168.0.10/255.255.255.0 und neuer MAC, auch mehrfach dieselbe Adresse; bei acht Karten gesperrt.
  • „-“: die letzte Karte samt Kabel; der Port am anderen Ende wird frei. Gesperrt erst bei null Karten. Das Panel zeichnet mit einer und mit keiner Karte; ohne Karte behält der Router kein Gateway (setStandardGateway schreibt es in die Karten, getStandardGateway liest null).

Das Fenster der Befehlszeile (F4.6b)

TerminalWindowRun öffnet Javas GUIApplicationTerminalWindow auf einem GUIDesktopPanel eines Rechners ohne Netz, gibt Tasten direkt an den Tastenhörer des Eingabefelds (Enter, Pfeil hoch/runter), stoppt und startet das System einmal (beenden, starten, wie ein Moduswechsel) und schreibt nach jeder Taste Text, Prompt, ob er sichtbar ist, und die Eingabezeile (packages/sim/test/terminalWindow.java.json).

Gemessen (03.10.2026):

  • Eine leere Zeile, auch nur aus Leerzeichen (StringUtils.isBlank), gibt „␣␣\n“; jede andere Zeile steht danach als „\n“ + Prompt + Zeile + „\n“ im Fenster, auch cls, reset und exit, bevor das Fenster sie selbst erledigt.
  • cls/reset zeigen wieder den Startbildschirm; exit schließt das Fenster, sein Text bleibt.
  • Pfeil hoch geht durch alle eingegebenen Zeilen zurück (leere nicht), runter vor, unter der neuesten steht eine leere Zeile; über die Enden hinaus bleibt es stehen.
  • Der Prompt folgt dem Arbeitsverzeichnis, nachgeführt, wenn ein Befehl fertig ist (cd dir → „/dir> “).
  • Nach Stopp und Start bleiben Text, Verlauf und Arbeitsverzeichnis: Javas Terminal setzt den Ordner nur bei der Installation (setSystemSoftware), und das Fenster ist dasselbe Objekt.

Software-Installation (F4.6c)

InstallRun installiert jedes Programm aus Javas Liste, wie GUIInstallationsDialog.aenderungenSpeichern es tut (installAndStartAppIfAvailable), auf einem eigenen neuen Rechner, alle auf einem („Alle“), jedes einmal mit anschließendem entferneSoftware („Entfernt-…“), dazu einen DNS-Server mit Eintrag und einen E-Mail-Server mit Konto, die entfernt und neu installiert werden („Wieder-…“); Java speichert das als Projekt (packages/doc/test/fixtures/installed-programs.fls).

Gemessen (03.10.2026):

  • Ein neues Programm ist aus (kein Server läuft); die Firewall eines Rechners dagegen ist sofort aktiv (verwerfen, UDP filtern).
  • Es legt an, was fehlt: der Webserver /webserver mit Javas Startseite in der Sprache, in der FILIUS läuft, und splashscreen-mini.png (nur, wenn noch keine index.html da ist), Gnutella /peer2peer, der E-Mail-Server /mailserver/konten.txt (leer), der DNS-Server /dns/hosts (leer).
  • Entfernt, verschwinden Programm und Einstellungen; die Dateien bleiben. Neu installiert, liest der DNS-Server seine Einträge aus /dns/hosts, der E-Mail-Server seine Konten aus konten.txt.
  • Gnutellas Teilnehmerzahl würfelt der Konstruktor (3 bis 5); die Datei verweist darauf mit idref auf irgendeine frühere gleiche Zahl — hier den Rückgabewert von iconTextGap (4) oder einer Thread-Priorität (5). Darum misst GetterDefaults jetzt auch #priority der gespeicherten Thread-Klassen (alle 5), die vorher unaufgelöst als 0 gelesen wurden.

Webbrowser und Webserver (F4.6d)

BrowserWindowRun öffnet Javas GUIApplicationWebBrowserWindow und GUIApplicationWebServerWindow auf je einem GUIDesktopPanel im Netz von web-lan (Browser 192.168.0.20, Webserver .30 mit eigenen Seiten) und benutzt sie wie eine Person: eine Adresse in die Adresszeile und Enter, ein Link (das Ereignis, das Swings HTMLEditorKit daraus macht: das href gegen die Basis des Dokuments), ein Formularknopf (der echte Knopf, den Swing für das Formular zeichnet, mit doClick), am Server Startknopf, Kästchen und Tabelle der virtuellen Hosts, dazu Stopp und Start des Systems. Nach jedem Schritt schreibt es Adresszeile, Titel, die Seiten, die das Programm dem Fenster gab, und die Anfragen des Servers, am Server Knopf, Kästchen, Tabelle, Datei und Zeilen (packages/sim/test/browserWindow.java.json).

Gemessen (03.10.2026):

  • Am Anfang zeigt die Adresszeile „http://“, der Titel den Programmnamen, die Seite Javas Logo. Jeder Abruf schreibt „http://“ + Host + Pfad ohne Query in Zeile und Titel; kam die Seite, wird ihr Titel der Titel des Fensters — als HTML („Links &amp; mehr“). Keine Adresse: die Zeile zurück auf „http://“, der Titel bleibt, nichts lädt.
  • Ein Query geht nie hinaus (HTTPNachricht.getPfad schneidet ihn ab): weder getippt noch aus einem Link noch aus einem GET-Formular. Ein POST-Formular schickt seine Felder in Reihenfolge samt gedrücktem Knopf, form-codiert (name=Fil+ius&c=1&s=y&t=%C3%BC%26%3D&ok=Gut).
  • Relative Links und Bilder zählen vom Wurzelordner des Servers: die Basis des Dokuments ist der Bildzwischenspeicher, nicht die Seite (ein Bild in /ordner/drei.html holt bild.png aus der Wurzel). Ein #anker lädt die Startseite des Hosts.
  • Bei einem Status außer 200 zeigt Java immer sw_webbrowser_msg2 („Fehler“): einlesenTextdatei löst den schon aufgelösten Pfad der Vorlage tmpl/http_fehler_* ein zweites Mal auf und findet sie nie. Ohne Server: sw_webbrowser_msg1. Text und Bilder zeigt das Fenster als HTML.
  • Der Webserver: „Starten“/„Anhalten“, das Kästchen schaltet die Tabelle ein (ohne Datei). Jede geänderte Zelle schreibt /www.conf/vhosts mit allen fünf Zeilen, leere als Leerzeilen; neu gelesen überspringt StringTokenizer die Leerzeilen, und die Zelle einer unvollständigen Zeile rückt in die davor. Liegt die Datei da, ist das Kästchen nach dem nächsten Start wieder an, auch abgeschaltet.