# 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).
```bash
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 (``), 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
`` 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 `.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-.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-.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-.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-.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.
## Modems zur Laufzeit
`ModemRun` verbindet auf dem Netz von `modem-lan` zwei Netze über ein
Modem-Paar: ein Server und ein Client auf `localhost`, Javas
`ModemFirmware` über einen echten TCP-Socket. `ScenarioNetwork` baut dazu
`Modem`s und stellt sie nach `modems` ein (Modus, Port).
`connectModems()` drückt den Knopf des Einstellungsfelds: erst lauschen die
Server, dann verbinden sich die Clients. 10.0.0.1 und 10.0.0.3 hängen an
einem Switch mit dem Server-Modem, 10.0.0.2 direkt am Client-Modem. Das
Werkzeug schreibt nach `modem-java-.java.json`. Abläufe: `ping`
(die Modems verbunden, zwei Pings von 10.0.0.1 an 10.0.0.2) und
`unconnected` (nicht verbunden, ein Ping).
Gemessen (11.10.2026):
* Das Modem hat **keine Karte**: Es erscheint in keinem Mitschnitt.
* Das Paar fügt **keine eigene Verzögerung** hinzu. Die ARP-Anfrage
braucht zu 10.0.0.3 zwei Kabel (124 ms), zu 10.0.0.2 drei (181 ms). Die
TCP-Strecke zwischen den Modems ist dagegen nicht messbar. Swifts 20 ms
je Richtung übernimmt der Kern deshalb nicht.
* Ohne Verbindung fragt ARP zweimal und bekommt keine Antwort, der Ping
endet nach 5,5 s mit „Host unreachable“ vom eigenen Rechner. Auf der
anderen Seite kommt nichts an.
Der Kern spielt beide Abläufe wie Java (`packages/core/test/modemJava.test.ts`).
Er paart die beiden Modems an einem Port, und das Modem ohne Partner
verwirft. Dass die Verzögerung genau null ist, prüft ein eigener Test: Die
Toleranz des Vergleichs (120 ms) sähe 20 ms nicht.
## 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-.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-.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-.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..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/.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-.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 `` 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 ``.
* `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 “, 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 & 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.
### E-Mail-Programm und E-Mail-Server (F4.6e)
`MailWindowRun` öffnet Javas `GUIApplicationEmailServerWindow` und
`GUIApplicationEmailAnwendungWindow` auf je einem `GUIDesktopPanel` im Netz
von mail-lan und benutzt sie wie eine Person: am Server Startknopf,
Domainfeld, das Formular „Neues Konto“ (der Knopf hört auf `mousePressed`,
nicht auf eine Aktion), Konten-Liste und Entfernen; im Programm den
Konto-Dialog, das Fenster zum Verfassen (seine Felder und Knöpfe auf dem
Desktop), Abrufen, eine gewählte Mail, Antworten, Löschen. Javas
Meldungsdialoge beantwortet ein Wächter-Thread wie eine Person (OK, „Ja“)
und schreibt ihren Text mit (`packages/sim/test/mailWindow.java.json`).
Gemessen (03.10.2026):
* Der Server: „Starten“/„Anhalten“; das Fenster selbst, nicht der Server,
schreibt „POP3- und SMTP-Server wurden gestartet.“/„… angehalten.“ ins
Log. Die Domain nimmt nur, was `musterDomain` nimmt, und nur angehalten
(das Feld ist sonst gesperrt); die Konten-Liste zeigt danach die neue
Domain, `konten.txt` die alte bis zur nächsten Kontenänderung.
* Neues Konto: ohne Name oder Passwort `emailserver_msg25`, mit Leerzeichen
`msg24`; das Formular hat das Etikett „Vorname und Nachname:“, aber kein
Feld — jedes Konto heißt „Vorname Nachname“. Ein vorhandener Name (auch
in anderer Schreibweise) wird nicht angelegt, das Fenster meldet trotzdem
„Das Benutzerkonto … wurde angelegt.“ Entfernen fragt erst.
* Das Programm: Der Konto-Dialog prüft die Ports (`musterPort`) und den
Benutzernamen (`musterEmailBenutzername`, leer geht), sonst
`emailanwendung_msg46`; der Name wird geteilt (letztes Wort Nachname) und
`/konten.txt` geschrieben. Verfassen prüft die Adressen (`msg20`) und
verlangt einen Empfänger (`msg21`); gesendet schließt das Fenster und
zeigt „Gesendete“, Abrufen den Posteingang. Zeilen: Absendername (sonst
Adresse) bzw. die Empfängerliste wie `holeEmpfaengerListe`; Antworten
schreibt an den Absender, „RE: …“ und den Text zitiert unter „… schrieb:“.
* Löschen löscht in Javas Fenster die Zeile der zuletzt **angeklickten**
Liste: War zuvor eine gesendete Mail gewählt und ist im Posteingang schon
eine markiert, geht die gesendete.
### Was ein Lauf im Projekt hinterlässt (F4.6f1)
`SaveAfterRunRun` öffnet ein Beispiel, wie FILIUS es öffnet, startet es,
spielt eine Geschichte über die Programme (wie ihre Fenster sie aufrufen),
stoppt wie der Entwurfsmodus (`stopSimulation`: `beenden` jedes Knotens) und
speichert (`SzenarioVerwaltung.speichern`; Java speichert nur im Entwurf):
`packages/sim/test/fixtures/after-run-email.fls` (email_komplett: Ordner
und Datei aus der Befehlszeile, Konten gesetzt, eine Mail gesendet und
abgerufen, ein neues Konto, Webserver aus und virtuelle Hosts an, ein
E-Mail-Server aus mit neuer Domain) und `after-run-dhcp.fls`
(einfaches_rechnernetz_komplett, 10 s DHCP).
Gemessen (03.10.2026):
* Java speichert die Dateien, wie die Programme sie hinterließen (auch
`/www.conf/vhosts`, die Kontendateien), ob jeder Server läuft, das
Kästchen der virtuellen Hosts (nach jedem Start an, wenn die Datei da
ist), Domain und Konten der E-Mail-Server, Konto, Posteingang und
Gesendete der E-Mail-Programme (eine abgerufene Mail mit „\n“ am Ende).
* Die Adressen, die DHCP-Clients bekamen, stehen danach in ihren Karten,
mit Gateway „0.0.0.0“, wo der Server keines vergibt.
* Die Konten der Programme in email_komplett liegen nur in der alten
`kontoListe`, die FILIUS 2.14 nicht liest; das Orakel setzt sie, wie man
sie in den Dialog tippt (`setzeKonto`, `speichern`).
### Der Texteditor (F4.6f2)
`TextEditorWindowRun` öffnet Javas `GUIApplicationTextEditorWindow` auf
einem `GUIDesktopPanel` (erster Rechner von mail-lan, mit Befehlszeile und
Webserver) und benutzt ihn wie eine Person: tippen, „Neu“, „Öffnen“,
„Speichern“, „Speichern unter“, dazu Javas Dateidialog (`DMTNFileChooser`:
die Liste des Ordners, ein Doppelklick auf einen Ordner oder eine Datei,
„Ordner aufwärts“, das Namensfeld, der eigene Knopf, „Abbrechen“). Der
modale Dialog wird gesucht, während der Knopf des Editors auf ihn wartet.
Daneben ändert die Befehlszeile die Dateien. Je Schritt: Titel und Text des
Fensters, was der Dialog zeigte, alle Dateien
(`packages/sim/test/textEditorWindow.java.json`).
Gemessen (03.10.2026):
* Der Titel ist „Text-Editor“, mit offener Datei „Text-Editor - name“; nach
„Öffnen“ in anderer Schreibweise der Name der Datei, nach „Speichern
unter“ der getippte.
* „Speichern“ ohne Datei fragt wie „Speichern unter“; der Dialog beginnt in
der Wurzel („Ordner: root“, sonst nur der Name des Ordners, nicht sein
Pfad), listet Ordner und Dateien in ihrer Entstehungsreihenfolge, ein
Doppelklick öffnet einen Ordner oder setzt den Namen einer Datei.
„Speichern unter“ legt eine Datei vom Typ `text` an.
* Ändert ein anderes Programm die offene Datei, übernimmt das Fenster sie,
solange nichts getippt ist; danach behält es seinen Text.
* „Öffnen“ nimmt den Namen als Pfad vom Ordner aus (`ordner/b.txt`), in jeder
Schreibweise; ein Bild erscheint als Base64-Text. Ohne Fund (auch ohne
Namen) vergisst das Fenster die offene Datei und behält den Text.
* Javas Fehler: „Speichern unter“ auf eine vorhandene Datei schreibt sie,
das Fenster bleibt aber in einer Kopie, die kein Ordner hält — jedes
spätere „Speichern“ geht verloren. Ebenso nach dem Löschen der offenen
Datei. „Speichern unter“ mit `ordner/d.txt` legt in der Wurzel eine Datei
mit `/` im Namen an, ohne Namen eine namenlose.
### Datei-Explorer und Bildbetrachter (F4.6f3)
`FileExplorerWindowRun` öffnet Javas `GUIApplicationFileExplorerWindow` und
`GUIApplicationImageViewerWindow` auf einem `GUIDesktopPanel` (erster
Rechner von mail-lan, mit Befehlszeile und Webserver) und benutzt sie wie
eine Person: den Baum, die Liste mit dem Menü eines Rechtsklicks (auf einem
Eintrag, auf der freien Fläche; ein gezeigtes Menü liegt in der Ebene des
Rahmens, nicht im Fenster), Javas Fragen — ein Wächter tippt in die
Eingabedialoge und sagt ja oder nein —, „Aktualisieren“, das Importfenster
(die Felder, wie „Datei wählen“ sie füllt); im Bildbetrachter „Öffnen“ über
den Dateidialog. Je Schritt: Zeilen des Baums, Liste, Menü, Texte der
Dialoge, Titel, Bilder des Betrachters, alle Dateien
(`packages/sim/test/fileExplorerWindow.java.json`).
Gemessen (03.10.2026):
* Der Baum zeigt Ordner und Dateien in Entstehungsreihenfolge, die Wurzel
als „root“; die Liste den gewählten Ordner, ohne „Aktualisieren“ nicht,
was die Befehlszeile anlegte.
* Das Menü auf einem Eintrag: Löschen, Ausschneiden, Kopieren, Umbenennen;
auf der freien Fläche: Neuer Ordner, mit etwas zum Einfügen auch
Einfügen. Kopiertes bleibt zum nächsten Einfügen, Ausgeschnittenes ist
nach dem Einfügen weg.
* Neuer Ordner fragt ohne Text, Umbenennen mit „Neuer Name:“ (ein
vorhandener oder leerer Name ändert still nichts), Löschen mit „Sicher?“.
* Das Importfenster: „Lokale Datei:“ (nur lesbar) mit „Datei wählen“, „Neuer
Name:“, „Datei importieren“, die Meldung darunter, „Schließen“.
* Javas Fehler: Eine im Baum gewählte Datei gilt als Ordner — die Liste ist
leer, ein neuer Ordner landet unter der Datei.
* Der Bildbetrachter heißt nach dem Öffnen wie die Datei; jedes geöffnete
Bild legt Java über das vorige, eine Textdatei zeigt es als leeres Bild,
ohne Fund bleibt alles.
### Echo-Server und Einfacher Client (F4.6g1)
`EchoWindowRun` öffnet Javas `GUIApplicationServerBausteinWindow` und
`GUIApplicationClientBausteinWindow` auf je einem `GUIDesktopPanel` im Netz
von mail-lan (Server 192.168.0.30, Client 192.168.0.20) und benutzt sie wie
eine Person: Port tippen und starten (auch „abc“), Adresse und Port tippen
und „Verbinden“ (auch Port „x“), Nachrichten senden (auch eine leere und
eine mit zwei Zeilen), den Server anhalten, während der Client verbunden
ist, „Trennen“, ein Port ohne Server, ein unbekannter Name. Je Schritt:
Knöpfe, Felder (und ob sie beschreibbar sind), Zeilen beider Fenster und die
Ausnahmen, die Javas Knöpfe warfen (`packages/sim/test/echoWindow.java.json`).
`SaveAfterRunRun echo` speichert danach, was Java vom Echo im Projekt
behält (`packages/sim/test/fixtures/after-run-echo.fls`).
Gemessen (03.10.2026):
* Server: „Starten“/„Anhalten“, Port 55555, das Feld nur angehalten
beschreibbar; ein Port, der keine Zahl ist, wirft
(`NumberFormatException`) und nichts geschieht.
* Client: Adresse leer, Port 55555; „Verbinden“/„Trennen“; „Senden“ nur
verbunden, danach ist das Nachrichtenfeld leer; die Felder sind verbunden
gesperrt und zeigen nach jeder Zeile wieder die Werte des Programms.
* Eine leere Nachricht geht als ein leeres Segment mit PSH hinaus
(`erstelleSegmente` macht mindestens eines) und kommt als „>>“, „<<“
zurück; Java beendet eine Nachricht mit jedem PSH, auch dem eines
wiederholten Segments.
* Der angehaltene Server schließt die Verbindung („Socket zu … geschlossen“,
beim Client „Socket zu … getrennt“). Ein Port ohne Server endet in
„Zeitüberschreitung beim Verbindungsaufbau“, ein unbekannter Name in „Ziel
konnte nicht bestimmt werden!“.
* Nach dem Lauf speichert Java den Port des Servers, dass er läuft, und
Adresse und Port, die der Client zuletzt nahm.
### DNS-Server (F4.6g2)
`DnsWindowRun` öffnet Javas `GUIApplicationDNSServerWindow` auf dem
DNS-Server von mail-lan (Einträge A, MX gemischt) und benutzt es wie eine
Person: Startknopf, Kästchen „Aktiviere rekursive Domain-Auflösung“, je
Reiter (A, MX, NS) die zwei Felder und „Hinzufügen“ (auch mit dem, was Java
nicht nimmt), eine Tabellenzelle bearbeitet (wie Javas Tabelle sie beim Ende
der Bearbeitung schreibt), eine Zeile gewählt und „Auswahl entfernen“;
daneben schreibt ein anderes Programm in `/dns/hosts`. Je Schritt: Knopf,
Kästchen, Felder, die drei Tabellen, die Datei, Javas Dialoge und was der
Resolver des ersten Rechners für einige Namen bekommt
(`packages/core/test/dnsWindow.java.json`).
Gemessen (03.10.2026):
* Javas DNS-Server liest `/dns/hosts` bei jeder Anfrage: Ein von außen
eingetragener Name wird sofort beantwortet. Angehalten laufen Anfragen in
die Zeitüberschreitung.
* Das Adressfeld zeigt anfangs die eigene Adresse; nach „Hinzufügen“ sind
beide Felder leer. Was Javas Muster nicht nehmen, meldet ein Dialog mit
einem gültigen Beispiel; eine ungültige Zelle geht still zurück.
Doppelte Einträge sind erlaubt. Ein neuer Eintrag bekommt seinen Punkt am
Namen (MX und NS auch am Ziel) und TTL 3600.
* Javas Fehler:
* Eine bearbeitete Zelle zählt nur die Einträge ihrer Art und schreibt
nach dem Treffer in jeden Eintrag anderer Art bis zum nächsten eigenen:
Der Mailserver eines MX-Eintrags landete als „Adresse“ im folgenden
A-Eintrag, der dann ungültig ist und beim nächsten Schreiben verschwand.
* „Hinzufügen“ schreibt die Liste zurück, die Java vorher gelesen hat:
Was ein anderes Programm inzwischen in die Datei schrieb, ist weg.
* Eine leere Maildomain wird genommen (`. MX 3600 x.`).
* „Auswahl entfernen“ entfernt den ersten Eintrag des Namens, bei zwei
gleichen Namen also womöglich den anderen.
### Firewall eines Rechners (F4.6g3)
`FirewallWindowRun` installiert auf dem Server von `firewall-pc` (zwei Netze
hinter einem Router, der Server mit DNS- und Echo-Server, je ein Rechner im
eigenen und im fremden Netz) Javas Firewall, öffnet ihr
`GUIApplicationFirewallWindow` und benutzt es wie eine Person: die drei
Kästchen, Port mit Protokoll- und Absenderliste und „Hinzufügen“ (auch mit
dem, was keine Zahl oder kein Port ist), eine Zeile gewählt und „Löschen“.
Je Schritt: die Kästchen (und ob sie sich ändern lassen), das Portfeld, die
Tabelle, das Protokoll (und ob man hineintippen kann); nach den Schritten,
die danach fragen, was vom eigenen und vom fremden Rechner beim Server
ankommt: ein Ping, eine TCP-Verbindung zum Echo-Server, eine DNS-Frage
(`packages/core/test/firewallWindow.java.json`). Ein Durchlauf dauert etwa
sieben Minuten (Javas Zeitüberschreitungen).
Gemessen (03.10.2026):
* Frisch installiert ist die Firewall aktiv, ICMP erlaubt, der UDP-Filter
an: Ein Ping geht durch, TCP und DNS nicht (Standardaktion verwerfen).
* Eine Regel für alle Absender lässt beide Rechner durch, „Absender im
gleichen Rechnernetz“ nur den eigenen. Jede greifende Regel steht im
Protokoll („Firewall-Regel hat gegriffen: #1 (…) -> akzeptieren“), auch
„Portregel wurde hinzugefügt: 55555/TCP“ und „Portregel wurde entfernt.“
* Ohne ICMP-Haken wird ein Ping verworfen und protokolliert, ohne
UDP-Filter geht DNS ohne Regel durch (ohne Zeile). Ist die Firewall aus,
lassen sich die beiden Kästchen nicht ändern, und alles geht durch.
* Javas Ungenauigkeiten:
* Jede ganze Zahl ist ein Port: −1 ergibt eine Regel für jeden Port
beider Protokolle (die Tabelle zeigt keinen Port), 0 und 70000 Regeln,
die nie greifen. Eine Zahl nach einem Leerzeichen lehnt Java still ab.
* Ins Protokoll kann man tippen.
* Den Erklärtext (`firewall_msg2`) baut Javas Fenster, zeigt ihn aber nie.
* Fallen der Sonde: Javas `ClientBaustein` hängt nach dem Trennen noch im
Empfangen, sein nächstes „Verbinden“ läuft dahinter nie — die Sonde nimmt
je Verbindung einen eigenen `TCPSocket`. Javas Echo-Server verträgt keine
sofort wieder geschlossene Verbindung (die folgenden laufen in die
Zeitüberschreitung) — die Sonde hält sie eine halbe Sekunde.
### Gnutella (F4.6g4)
`GnutellaWindowRun` öffnet Javas `GUIApplicationPeerToPeerAnwendungWindow`
auf dem ersten Rechner von `terminal-net`, neben der Kette aus
`GnutellaRun`s Geschichte „search“ (.30 und 10.0.0.2 sind .31 beigetreten,
.31 teilt `notizen.txt`, 10.0.0.2 „Lied der Woche.txt“), und benutzt es wie
eine Person: Adresse (auch keine) und „Netz beitreten“, Suchbegriff mit
„Suchen“ und „Stopp!“, ein Ergebnis gewählt und „Herunterladen“, „Liste
leeren“, die Zahl der Teilnehmer mit „Speichern“ (auch keine Zahl), „Netz
verlassen“. Javas Knöpfe hören auf `mousePressed`, die Felder prüfen bei
`keyReleased` — die Sonde schickt beides. Je Schritt: was das Fenster zeigt
(Adressfeld und ob es rot ist, die drei Tabellen, das Feld der Teilnehmer)
und was das Programm hält (Zahl, Teilnehmer, Ergebnisse, Dateien von
`/peer2peer`) (`packages/core/test/gnutellaWindow.java.json`).
Gemessen (03.10.2026):
* Das Adressfeld beginnt mit 192.168.0.1; keine Adresse wird rot, „Netz
beitreten“ meldet dann „Dies ist keine gültige IP-Adresse“. Die eigene
Adresse tut nichts. Das Feld der Teilnehmer zeigt die Zahl des Programms.
* „Stopp!“ kurz nach „Suchen“: kein Treffer kommt an; eine neue Suche
findet wieder. Ohne gewählte Zeile tut „Herunterladen“ nichts; ein zweites
Herunterladen überschreibt die Datei.
* „Netz verlassen“ leert die Nachbarn, nicht die Ergebnisse.
* Javas Ungenauigkeiten:
* Die Prüfung der Teilnehmerzahl (`musterNurZahl` = `\d`) nimmt nur eine
Ziffer: 12 wird mit „Es sind nur Zahlen erlaubt!“ abgelehnt, 0 genommen.
* Nach einer Suche ohne Treffer zeigt die Ergebnistabelle weiter die
Ergebnisse der vorigen (das Programm hat keine mehr), bis etwas anderes
das Fenster auffrischt.
* Die Dateitabelle zeigt eine heruntergeladene Datei erst, wenn das
Programm das nächste Mal Bescheid gibt; eine daneben angelegte gar nicht.
### SAT- und NAT-Anzeige, „Pakete verwerfen“ (F4.6g5)
`ViewersRun` misst, was Javas Anzeigen der laufenden Simulation zeigen. Die
Anzeigen (`SatViewer`, `NatViewer`, `SimulationControl`) zeigen nur Zeilen
der Firmware; die Sonde nimmt sie dort ab (`holeSAT`, `holeNAT`,
`getDroppedDataUnits`). Geschichte `switch` auf `switch-lan` (Haltezeit 5 s):
zwei Pings, die Tabelle, „Tabelle löschen“, ein Ping, nach der Haltezeit;
dann „Pakete verwerfen“ gedrückt (`Verbindung.setDrop`) während eines Pings
und losgelassen. Geschichte `nat` auf `nat-home`: die Portweiterleitung, ein
Ping und ein Datagramm hinaus, „Tabelle zurücksetzen“
(`packages/core/test/viewers-*.java.json`).
Gemessen (03.10.2026):
* Die Switch-Tabelle zeigt die Adresse groß, „Port “ nach der Stelle des
Anschlusses und die Uhrzeit der letzten Meldung (`HH:mm:ss`), in der
Reihenfolge von Javas Hash-Tabelle; nach der Haltezeit ist sie leer.
* Die NAT-Tabelle zeigt die Portweiterleitung mit „*“ und „-“, einen Ping
als `0 (Identifier: 1)`, ein Datagramm mit gewürfeltem WAN-Port; „Tabelle
zurücksetzen“ lässt nur die Portweiterleitung stehen.
* „Pakete verwerfen“ wirkt nur, solange der Knopf gedrückt ist; die Liste
zeigt `HH:mm:ss.SSS : ` und den Frame in Javas Textform
(`[src=…, dest=…, type=0x800 | [id=…; ttl=64; …]]`; ein IP-Paket ohne sein
Segment).
### Datenaustausch-Fenster (F4.7)
`ExchangeWindowRun` benutzt Javas Datenaustausch-Fenster wie eine Person:
`AggregatedExchangeDialog`, das `filius.ini` wählt (`old-exchange-dialog=0`),
mit den Details als `MessageDetailsPanel` (ohne die experimentelle
Schichtenansicht, Javas Voreinstellung). Auf `web-lan` öffnet die Sonde die
Karte des Browsers vor jedem Verkehr, lädt `www.filius.de/text.txt`, wählt
jede Zeile und liest die Details, exportiert die Tabelle (`writeToStream`),
öffnet die Karte des Servers daneben und die erste wieder, wählt „Tabellen
löschen“ aus dem Menü der Tabelle (per `mousePressed`), lädt noch einmal
und startet die Simulation ohne den Server neu (als wäre er im Entwurf
entfernt); dann wird das Fenster aktiviert (`clearUnavailableComponents`).
Nach jedem Schritt: Reitertitel, gewählter Reiter, die Zeilen jeder Tabelle
und zum Vergleich `Lauscher.getDaten` (`packages/core/test/exchangeWindow.java.json`).
Gemessen (04.10.2026):
* Die Tabelle zeigt je Frame eine Zeile, die seiner höchsten Schicht (mit
Adresse:Port); 17 Zeilen statt Javas 51 Schichtzeilen.
* Reitertitel „Anna - 192.168.0.20“, beim Rechner mit der Adresse als
Beschriftung nur „192.168.0.30“; ein zweites Öffnen wählt den Reiter.
* Die Details: „Nr.: 1 / Zeit: 08:32:35.815“, je Schicht ihr Name, darunter
`Quelle:`, `Ziel:`, `Protokoll:` auf 15 Zeichen aufgefüllt (leere fehlen),
„Bemerkungen / Details: …“, bei der Anwendung „(83 Bytes)“; eine
Bemerkung mit Zeilenumbruch oder über 60 Zeichen als eigener Knoten.
* Der Export: Rahmen aus `=` um den Kopf, Breiten 10/20/40, die Bemerkung
auf Wörter zu je höchstens 40 Zeichen, Zeilen mit CR LF.
* „Tabellen löschen“ leert die Tabellen aller Karten; danach beginnen die
Nummern bei 1. Das Menü: „Tabellen löschen“, „Als Textdatei exportieren“,
„Automatisches Scrollen“.
* Nach dem Neustart bleiben der Reiter des Browsers und seine 13 Zeilen;
der Reiter des nicht gestarteten Servers verschwindet.
Fallen der Sonde: Die Einträge des Tabellenmenüs hören nur auf
`mousePressed` (sie haben keinen `ActionListener`). Beim Stoppen versteckt Javas Oberfläche das Fenster
(`closeDialogs`); ein Aktivieren im Entwurf, wie es die Sonde zuerst tat,
entfernt alle Reiter, kommt aber in Javas Oberfläche nicht vor.
### Dokumentation: Text und Gliederungsfläche (F4.8a)
`DocuRun` misst Javas Dokumentationsmodus an seinen eigenen Elementen
(`JDocuElement` auf einem `GUIDocumentationPanel`, wie das Ziehen aus der
Seitenleiste sie macht): neue Elemente, ihr Menü (per Reflection
`showPopupMenu`, Einträge per `doClick`, Farben über
`triggerMenuResponse`), die kleinste Größe nach einem Ziehen am Rand
(MouseEvents `moved`/`pressed`/`dragged`), die Pixel eines allein gemalten
Elements in jedem Zustand, die Stapelung nach `updateViewport` und ein
Projekt, das Java mit `SzenarioVerwaltung.speichern` schreibt und wieder
lädt (`packages/doc/test/documentation.java.json`,
`packages/doc/test/fixtures/documentation.fls`).
Gemessen (04.10.2026):
* Neu: 100 × 50; ein Text leer, schwarz, Dialog 12 normal; eine Fläche grau
(204, 204, 204). Ein abgelegter Text bekommt den Fokus, sein Rahmen wächst
um 6 nach links/oben, 12 in der Breite, 28 in der Höhe; gespeichert wird
der Rahmen ohne Fokus.
* Menü eines Textes: „Schriftgröße“ (Normal/Groß/Sehr Groß = 12/18/24),
„Schriftstil“ (Normal/Kursiv/Fett, je einer: Kursiv dann Fett ist Fett),
„Farbe“ (15 Farben in drei Reihen), Trenner, „Löschen“. Einer Fläche:
„Farbe“ (12 Farben), Trenner, „Löschen“. Die aktuelle Wahl ist markiert.
* Kleinste Größe nach einem Ziehen: Fläche 20 × 20; ein Text wird nur im
Fokus gezogen, sein vergrößerter Rahmen bis 100 × 48, der Text also bis
88 × 20.
* Gemalt im Dokumentationsmodus: Fläche in ihrer Farbe mit Deckkraft 200,
Rand in 4/5 ihrer Farbe, Ecken rund; im Fokus ein blauer Rahmen (122, 186,
250) mit durchscheinendem Band. In Entwurf und Aktion deckend, um 20 % zu
Weiß hin aufgehellt (255, 201, 201 → 255, 212, 212), derselbe Rand. Ein
Text hat nur im Dokumentationsmodus einen grauen Rand (179).
* Stapel: Texte über Flächen, je Art das frühere Element oben.
* Gespeichert und geladen: Kursiv (Stil 2), Fett (1), Farben und Größen
bleiben; eine Fläche hat keine Schrift.
Falle der Sonde: Der Menüpunkt „Normal“ steht in beiden Untermenüs; die
Sonde nimmt den ersten (Schriftgröße).
### Dokumentation: „Exportieren“ (F4.8c)
`ExportRun` öffnet ein Projekt, wie Javas Menü es öffnet
(`Information.reset`, `SzenarioVerwaltung.laden`, `updateViewport`,
`updateCables`), holt per Reflection das Druckpanel
(`GUIContainer.prepareExportPanel`) und liest dessen Ausschnitt
(`getClipX` … `getClipHeight`), die Grenzen seiner drei Ebenen (Netz,
Dokumentation, Fußzeile; `GUIMainArea.minX` …) und die Fußzeile. Dann malt es
mit `createNetworkImage` das Bild und misst Größe, Eckpixel, die Farben der
Fußzeile und die Füllung einer Fläche darin. Vier Projekte
(`nat_with_mapped_port`, `zwei_rechnernetze_komplett`, die Fixture
`documentation.fls`, `modem_beispiel`) und ein leeres
(`packages/editor/test/networkImage.java.json`; Javas Bilder unter
`.build/export`).
Gemessen (04.10.2026):
* Ausschnitt: alle Geräte, Kabel (die beiden Mitten mit 2 Rand) und
Dokumentationselemente, links, oben und rechts 10 Rand. Die Fußzeile steht
20 unter dem untersten Element, bündig mit dem linkesten; sie ist so breit
wie ihr Text und 20 mehr, so hoch wie ihre Schrift; unter ihr 20 Rand. Ist
die Fußzeile breiter als der Inhalt, reicht das Bild 20 über sie hinaus.
* Fußzeile: der Pfad der Datei, ohne Datei „Filius“, hellgrau (192).
* Hintergrund durchsichtig; Dokumentation unter dem Netz, gemalt wie im
Entwurf (Fläche 204 → 215, 201 → 212); nichts ausgewählt.
* Leeres Projekt: ein leeres Bild 20 × 35. Die Fußzeile landet durch einen
Überlauf (`Integer.MAX_VALUE + 10`) außerhalb. Nicht übernommen.
Falle der Sonde: Das Druckpanel nimmt Javas Geräte-Etiketten aus der
Oberfläche (eine Swing-Komponente hat nur einen Elternteil); erst
`updateViewport` legt sie zurück.
### Dokumentation: „Report“ (F4.8d)
`ReportRun` öffnet ein Projekt wie Javas Menü, lässt es auf Wunsch eine
Weile laufen (alle Knoten `starten`, dann `beenden`; der Lauscher behält die
Rahmen wie nach einer Simulation) und schreibt mit
`ReportGenerator.generateReport` Javas PDF. Das liest es mit iTexts eigenem
Parser zurück: jedes Textstück mit Ort, Schrift, Größe (aus dem privaten
Grafikzustand per Reflection) und Farbe, jedes Bild mit Ort und Größe, Titel
und Ersteller. Drei Projekte (`einfaches_rechnernetz_komplett` nach 3 s
Lauf, `Internet_Komplett_…_DE`, die Fixture `documentation.fls`) und ein
leeres (`packages/editor/test/report.java.json`). Mit `--read` liest es ein
beliebiges PDF allein, mit SHA-256: so liest Javas iText den Bericht des
Editors (`report.ours.java.json`).
Gemessen (04.10.2026):
* A4, 36 Rand; Titel Helvetica-Bold 18 in (30, 50, 120), mittig; darunter
der Pfad in Helvetica 8; „1. Übersicht“ mit Javas Bild in Seitenbreite,
eine halbe Zeile unter der Überschrift; neue Seite „2. Komponenten“; neue
Seite „3. Datenaustausch“, nur wenn der Lauscher Rahmen hat.
* Überschriften Helvetica-Bold 14/12/10 in Dunkelgrau (64), Ebene 1 und 2
nummeriert („2.1. “), Ebene 3 nicht. Zeilenabstand das 1,5-Fache der
Schrift; lose Textstücke (`Chunk`) laufen in derselben Zeile mit dem
Abstand des letzten Absatzes, bis `Chunk.NEWLINE` sie beendet.
* Tabellen: 80 % der Breite, links, Spalten im Verhältnis (180:360,
3 × 180, 180:60:260, 4 × 180); Zellen mit 2 Innenabstand, Zeilen so weit
auseinander wie die Schrift groß ist; eine Zeile, die unter den Rand
reicht, beginnt die nächste Seite (ohne Kopfzeile). Datenaustausch: feste
Spalten 20/60/80/80/40/50/180, mittig, Helvetica 8, Kopf hellblau (230,
240, 255), jede zweite Zeile grau (240).
* Je Gerät mit Karten (nicht Switch, nicht Modem): Gateway, DNS-Server, bei
Rechnern „DHCP zur Konfiguration verwenden“ als `true`/`false`; Karten;
bei Rechnern „Installiert: “ und die Programme (oder „-“), in der
Reihenfolge einer HashMap; mit DNS-Server dessen Einträge;
Weiterleitungstabelle (nur Zeilen mit einer Adresse als Ziel); beim
Heimrouter die Portweiterleitung, wenn eine Zeile ein bekanntes Protokoll
hat. Datenaustausch je Karte mit Rahmen („Name - Adresse“), die Zeilen
von `Lauscher.getDaten(id, true, 0)`, in der Reihenfolge einer HashMap.
* Leeres Projekt: Titel ohne Namen, kein Pfad; das leere 20 × 35-Bild wird
auf Seitenbreite 915 hoch und läuft über den Rand einer eigenen Seite.
* Java-Fehler am Rande: der Bericht schreibt die Protokolle der
Portweiterleitung („6“ → „TCP“) ins Modell zurück und gibt eine
Debug-Zeile auf der Konsole aus.
Fallen der Sonde: iText legt Absätze und Tabellen in eigene Ebenen, die
Zeichenreihenfolge im PDF ist darum nicht die der Seite (der Test vergleicht
nach Ort). Die Uhrzeiten des Datenaustauschs sind Javas Wanduhr; der Test
nimmt Javas Zeilen aus Javas Seiten, damit der Satz verglichen wird, nicht
der Lauf.