feat(core): Gnutella joining after Java, measured against Java
GnutellaRun lets Java's PeerToPeerAnwendung join peers on terminal-net. The core's gnutella.ts does the same: a ping in Java's text format to port 6346, a pong back, the ping sent on to the other peers and their pongs back the way it came, at most the program's number of peers. Departure from Java: a ping that comes a second time gets no pong and its connection stays (Java's worker throws on the missing pong). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -870,6 +870,41 @@ Gemessen (02.10.2026):
|
||||
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.
|
||||
|
||||
## Datei-Explorer, Texteditor, Bildbetrachter (F3.5b)
|
||||
|
||||
`FileCases` tut, was die Fenster dieser Programme mit den Dateien eines
|
||||
|
||||
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
|
||||
fi
|
||||
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
|
||||
mkdir -p "$here/.build"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java"
|
||||
# FILIUS writes its settings below $HOME; keep them out of the real one.
|
||||
mkdir -p "$here/.build/home"
|
||||
# Removed first: a crashed run must not leave the previous result standing.
|
||||
@@ -286,6 +286,17 @@ for language in de en; do
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# The Gnutella program joining peers on terminal-net (GnutellaRun;
|
||||
# packages/core/test/gnutellaJava.test.ts).
|
||||
for run in pair:6000 chain:9000 twoPaths:15000 full:11000 reset:7000 errors:40000; do
|
||||
story="${run%%:*}"
|
||||
output="$root/packages/core/test/scenarios/gnutella-java-$story.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java $german -cp "$classpath:$here/.build" GnutellaRun \
|
||||
"$root/packages/core/test/scenarios/terminal-net.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at \|scrollpane' || true
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# What the file explorer and the text editor do to a host's files, without a
|
||||
# running network, in both languages (FileCases; packages/core/test/filePrograms.test.ts).
|
||||
for language in de en; do
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// Runs a story of FILIUS' Gnutella program (PeerToPeerAnwendung with its
|
||||
// PeerToPeerServer on port 6346 and PeerToPeerClient) on terminal-net in Java
|
||||
// FILIUS and records what really happens: every frame each card saw, Java's
|
||||
// tables of them, each program's known peers whenever it tells its window,
|
||||
// and at the end each program's known peers and search results.
|
||||
//
|
||||
// GnutellaRun <scenario.json> <story> <milliseconds> <out.json>
|
||||
//
|
||||
// terminal-net: the program on 192.168.0.20, .30, .31 and 10.0.0.2 (behind
|
||||
// the router), each keeping at most three peers unless the story says
|
||||
// otherwise; .10 has none. A call comes as the program's window makes it
|
||||
// (beitretenNetzwerk, resetNetwork); the acts are in the output.
|
||||
// pair .30 joins .31
|
||||
// chain .30 joins .31, then .20 joins .30 (the ping goes on to .31)
|
||||
// twoPaths .31 joins 10.0.0.2, .30 joins .31 and 10.0.0.2, then .20 joins
|
||||
// .30: the ping reaches .31 and 10.0.0.2 each on two paths
|
||||
// full .31 keeps two peers, .30 and .20 one; .30, .20 and 10.0.0.2 join .31
|
||||
// reset .30 joins .31, resets, joins again
|
||||
// errors .20 joins itself, .10 (no program), .99 (no one)
|
||||
|
||||
import filius.software.dateiaustausch.PeerToPeerAnwendung;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GnutellaRun {
|
||||
static final String CLIENT = "00000000-0000-0000-0000-000000000401";
|
||||
static final String WEB = "00000000-0000-0000-0000-000000000403";
|
||||
static final String ECHO = "00000000-0000-0000-0000-000000000404";
|
||||
static final String FAR = "00000000-0000-0000-0000-000000000406";
|
||||
static final String[] PEERS = { CLIENT, WEB, ECHO, FAR };
|
||||
static final String CLASS = "filius.software.dateiaustausch.PeerToPeerAnwendung";
|
||||
|
||||
static ScenarioNetwork network;
|
||||
static final List<Object[]> events = new ArrayList<>();
|
||||
|
||||
static synchronized void event(String host, String kind, String text) {
|
||||
events.add(new Object[] { System.currentTimeMillis() - network.start, host, kind, text });
|
||||
}
|
||||
|
||||
static void sleepUntil(long milliseconds) throws InterruptedException {
|
||||
long rest = network.start + milliseconds - System.currentTimeMillis();
|
||||
if (rest > 0) Thread.sleep(rest);
|
||||
}
|
||||
|
||||
/** The story's calls: time, host, call ("join <address>" or "reset"). */
|
||||
static List<Object[]> acts(String story) {
|
||||
List<Object[]> acts = new ArrayList<>();
|
||||
switch (story) {
|
||||
case "pair":
|
||||
acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" });
|
||||
break;
|
||||
case "chain":
|
||||
acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" });
|
||||
acts.add(new Object[] { 3000L, CLIENT, "join 192.168.0.30" });
|
||||
break;
|
||||
case "twoPaths":
|
||||
acts.add(new Object[] { 500L, ECHO, "join 10.0.0.2" });
|
||||
acts.add(new Object[] { 3000L, WEB, "join 192.168.0.31" });
|
||||
acts.add(new Object[] { 5500L, WEB, "join 10.0.0.2" });
|
||||
acts.add(new Object[] { 8000L, CLIENT, "join 192.168.0.30" });
|
||||
break;
|
||||
case "full":
|
||||
acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" });
|
||||
acts.add(new Object[] { 3000L, CLIENT, "join 192.168.0.31" });
|
||||
acts.add(new Object[] { 5500L, FAR, "join 192.168.0.31" });
|
||||
break;
|
||||
case "reset":
|
||||
acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" });
|
||||
acts.add(new Object[] { 3000L, WEB, "reset" });
|
||||
acts.add(new Object[] { 3500L, WEB, "join 192.168.0.31" });
|
||||
break;
|
||||
case "errors":
|
||||
acts.add(new Object[] { 500L, CLIENT, "join 192.168.0.20" });
|
||||
acts.add(new Object[] { 1000L, CLIENT, "join 192.168.0.10" });
|
||||
acts.add(new Object[] { 20000L, CLIENT, "join 192.168.0.99" });
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown story " + story);
|
||||
}
|
||||
return acts;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
network = ScenarioNetwork.read(args[0]);
|
||||
String story = args[1];
|
||||
long end = Long.parseLong(args[2]);
|
||||
List<Object[]> acts = acts(story);
|
||||
|
||||
Map<String, PeerToPeerAnwendung> programs = new LinkedHashMap<>();
|
||||
for (String host : PEERS) {
|
||||
network.system(host).installApp(CLASS, false);
|
||||
PeerToPeerAnwendung program = (PeerToPeerAnwendung) network.system(host).holeSoftware(CLASS);
|
||||
program.setMaxTeilnehmerZahl(!story.equals("full") ? 3 : host.equals(ECHO) ? 2 : host.equals(FAR) ? 3 : 1);
|
||||
program.hinzuBeobachter((observable, data) -> event(host, "peers", String.join(",", program.holeBekanntePeerToPeerTeilnehmer())));
|
||||
programs.put(host, program);
|
||||
}
|
||||
network.start();
|
||||
for (Object[] act : acts) {
|
||||
sleepUntil((Long) act[0]);
|
||||
String host = (String) act[1];
|
||||
String call = (String) act[2];
|
||||
event(host, "call", call);
|
||||
if (call.startsWith("join ")) programs.get(host).beitretenNetzwerk(call.substring("join ".length()));
|
||||
else programs.get(host).resetNetwork();
|
||||
}
|
||||
sleepUntil(end);
|
||||
|
||||
ScenarioNetwork.Json out = new ScenarioNetwork.Json();
|
||||
out.open('{');
|
||||
network.writeFrames(out);
|
||||
out.key("acts").open('[');
|
||||
for (Object[] act : acts) out.open('[').number((Long) act[0]).string((String) act[1]).string((String) act[2]).close(']');
|
||||
out.close(']');
|
||||
synchronized (GnutellaRun.class) {
|
||||
out.key("events").open('[');
|
||||
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).string((String) each[3]).close(']');
|
||||
out.close(']');
|
||||
}
|
||||
out.key("programs").open('{');
|
||||
for (Map.Entry<String, PeerToPeerAnwendung> entry : programs.entrySet()) {
|
||||
PeerToPeerAnwendung program = entry.getValue();
|
||||
out.key(entry.getKey()).open('{');
|
||||
out.key("peers").open('[');
|
||||
for (String peer : program.holeBekanntePeerToPeerTeilnehmer()) out.string(peer);
|
||||
out.close(']');
|
||||
out.key("results").open('[');
|
||||
for (String result : program.holeErgebnisse()) out.string(result);
|
||||
out.close(']');
|
||||
out.close('}');
|
||||
}
|
||||
out.close('}');
|
||||
out.close('}');
|
||||
network.stop();
|
||||
ScenarioNetwork.write(args[3], out);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user