Rechnerkommunikation und -netze

Rechnerkommunikation und -netze Praktikumsaufgabe Wintersemester 2015/16 Jörg Roth Rechnerkommunikation und -netze - Praktikumsaufgabe Wintersemest...
Author: Clemens Schmidt
4 downloads 0 Views 861KB Size
Rechnerkommunikation und -netze Praktikumsaufgabe Wintersemester 2015/16

Jörg Roth

Rechnerkommunikation und -netze - Praktikumsaufgabe Wintersemester 2015/2016

Prof. Dr. habil. Jörg Roth TH Nürnberg Fakultät Informatik Hohfederstraße 40 90489 Nürnberg

Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Autors urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen. Es wird darauf hingewiesen, dass die hier verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen. Alle Angaben und Programme wurden mit größter Sorgfalt kontrolliert. Der Autor kann jedoch nicht für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieser Publikation stehen.

2

Inhalt 1. Einleitung ................................................................................................................ 4 2. Das Sliding-Window-Verfahren ................................................................................ 4 3. Die Rahmenstruktur ................................................................................................. 5 4. Was ist zu entwickeln .............................................................................................. 5 5. Die Simulationsumgebung ....................................................................................... 6 6. Was ist abzuliefern................................................................................................... 8 7. Weitere Hinweise..................................................................................................... 9 7.1 Hilfsklasse, Bibliotheken, Annotations ................................................................. 9 7.2. Kodierung von Inhalten in Rahmen-Feldern........................................................ 9 7.3. Instanzen des NetworkCard-Objektes .............................................................. 11 7.4. Busy-Waiting-Loops......................................................................................... 11 7.5 Binär-Addition.................................................................................................. 13 7.6 Threads ............................................................................................................ 13

3

1. Einleitung Entwickeln Sie ein Sliding-Window-Verfahren für ein Layer-2-Protokoll. Dieses Verfahren soll bei der Übertragung einer Datei zum Einsatz kommen. Die Aufgabe umfasst die Entwicklung von Sender und Empfänger der Datei. Um Effekte zu simulieren, wie sie in einem Direktverbindungsnetzwerk auftreten können, steht eine Simulationsumgebung zur Verfügung. Diese streut Rahmenverzögerungen und Übertragungsfehler zufällig in eine Übertragung ein. Die Fehlerrate und die Verzögerungen sind dabei konfigurierbar. Die Entwicklung erfolgt in Java.

2. Das Sliding-Window-Verfahren Das Sliding-Window-Verfahren soll analog zur Vorlesung realisiert werden. Abb. 1 illustriert noch einmal das Verfahren.

Abb. 1: Illustration des Sliding-Window-Verfahrens

Es gelten folgende Festlegungen:  Sende- und Empfangsfenster haben die Größe 4.  Es werden kumulative ACKs verwendet (insb. keine negativen ACKs).  Es sollen keine Duplicate ACKs erkannt werden, d.h. die Neu-Sendung soll ausschließlich auf Basis des Timeouts erfolgen.  Über eine Prüfsumme gemäß dem TCP/IP-Prüfsummenverfahren sollen Fehler erkannt werden. Die Prüfsumme muss Kopf- und Nutzdaten sichern.  Der Timeout für die Sendewiederholung beträgt 200ms.

4

3. Die Rahmenstruktur Bestandteil der Aufgabe ist, eine geeignete Rahmenstruktur zu entwerfen.  Der Rahmen muss mindestens ein Prüfsummenfeld, Quell-, Ziel-Adresse enthalten. Weitere Felder werden sicher durch das Sliding-Window-Verfahren notwendig sein.  Die Rahmenstruktur soll Nutzdatenbereiche variabler Größe unterstützen.  Auch ACKs sollen in diesem Rahmenformat darstellbar sein. Wählen Sie eine geeignete Größe für die jeweiligen Felder aus. Dokumentieren Sie ihre Rahmenstruktur in einer Textdatei frame.txt. Für jedes Feld des Rahmens soll eine Textzeile mit folgendem Aussehen vorkommen: -: Feldname Nimmt ein Feld darüber hinaus diskrete Werte an, müssen diese in der Zeile aufgeschlüsselt werden. Abb. 2 zeigt ein (nicht so ernst gemeintes) Beispiel für die geforderte Rahmendokumentation.

0-1: Hausnummer des Senders 2-5: Dioptrie-Wert des Senders 6-6: Status des Senders: 0: müde 1: wach 2: hellwach 7-Ende: Nutzdatenbereich Abb. 2: Beispiel einer Rahmen-Beschreibung

4. Was ist zu entwickeln Es soll ein Sender- und ein Empfänger-Programm entwickelt werden. Der Sender sendet eine Reihe von Nutzdaten-Rahmen nach dem Sliding-Window-Verfahren aus. Der Empfänger quittiert erfolgreich empfangene Rahmen und verarbeitet die Rahmen in der richtigen Reihenfolge. Es ist nicht vorgesehen, dass Sender und Empfänger ihre Rollen tauschen. Sender und Empfänger müssen auch nur eine einzelne Verbindung unterstützen. Der Empfänger muss beispielsweise nicht darauf gefasst sein, dass simultan ein weiterer Sender eine Übertragung beginnt. Sender und Empfänger bekommen feste Adressen zugeteilt. Rahmen, die unerwartete Quell- oder Zieladressen haben, müssen verworfen werden. Zum Sender:  Der Sender öffnet die Textdatei input.txt und liest diese zeilenweise aus. Die Zeilen werden mit CR/LF (Code 13/10 oder 0x0D/0x0A) beendet (auch die letzte).  Für jede Zeile (exkl. Zeilenende-Zeichen) wird ein Rahmen generiert. Die Textzeile muss in geeigneter Weise auf den Nutzdatenteil des Rahmens abgebildet werden.  Der Sender arbeitet in einer Schleife alle Zeilen des Textes ab und initiiert jeweils einen Sendevorgang. Ist der Sendepuffer voll, muss der Sender blockieren, d.h. die Sendemethode muss den Aufrufer solange in den Wartezustand versetzen, bis der Sendepuffer wieder einen weiteren Rahmen aufnehmen kann.  Wenn die gesamte Textdatei versendet wurde und alle Rahmen quittiert wurden, soll das Sendeprogramm terminieren.  Der Sender soll die feste Adresse 1655 haben.  Die Hauptklasse des Sender-Programms hat den Namen RNSender. 5

Zum Empfänger:  Der Empfänger speichert Rahmen, die nicht in der erwarteten Reihenfolge eintreffen, in einem Empfangspuffer (mit der Größe des Empfangsfensters) zwischen.  Rahmen, die außerhalb des Empfangsfensters liegen, werden verworfen.  Rahmen in der richtigen Reihenfolge werden verarbeitet. Das bedeutet hier: die Textzeile wird aus der Rahmenstruktur gelesen und zeilenweise (d.h. zuzüglich CR/LF) in eine Textdatei output.txt gespeichert.  Der Empfänger soll nicht terminieren, wenn die Textdatei komplett empfangen wurde (woher sollte der Empfänger auch erkennen, dass die Datei zuende ist). Daher muss die Datei output.txt nach jeder Textzeile festgeschrieben werden.  Der Empfänger soll die feste Adresse 3261 haben.  Die Hauptklasse des Empfänger-Programms hat den Namen RNReceiver. Sender und Empfänger sind reine Konsolenanwendungen. Sie dürfen keine Kommandozeilenparameter oder Eingaben erwarten. Sie sollen für jedes relevante Ereignis eine geeignete Textmeldung (z.B. "ACK für Rahmen 123 empfangen") auf der Konsole ausgeben. Zur Nebenläufigkeit: Auf Senderseite kann vieles nebenläufig erfolgen, z.B. kann ein Rahmen ausgesendet werden, gleichzeitig wird ein ACK empfangen und ein Timeout ist ablaufen. Bitte machen Sie sich mit Mechanismen zur Nebenläufigkeitskontrolle in Java vertraut. Hierzu einige Stichworte:  Die Klasse Thread erlaubt, Methoden nebenläufig auszuführen. Ein Thread wird übrigens mit start() und nicht mir run() gestartet.  Das Schlüsselwort synchronized verhindert die gleichzeitige Abarbeitung von Code-Blöcken durch verschiedene Threads.  Über die Methoden wait() und notify() können sich Threads über das Eintreffen von Bedingungen gegenseitig informieren, ohne dass eine Bedingung 'busy-waiting' geprüft werden muss.  Thread.sleep(ms) erlaubt Threads eine bestimmte Zeit zu blockieren. Hiermit kann eine zeitliche Taktung von Abläufen formuliert werden. Auf Seiten des Empfängers ist keine Nebenläufigkeit zu erwarten.

5. Die Simulationsumgebung Für die Praktikumsaufgabe wird eine Umgebung bereitgestellt. Diese besteht aus  einer Bibliothek, um mit einer virtuellen Netzwerkkarte zu kommunizieren und  einem Programm, das Rahmen transportiert. Das kann Rahmen verzögern und Zufallsfehler produzieren. Idee:  Man startet ein Java-Programm, das Rahmen transportiert, das so genannte Distribution System. Abb. 3 zeigt einen Aufruf zum Start – die Erklärung zu den Aufrufparametern folgt später.

6

java -cp .;rn.jar rn.DistributionSystem delay_from=0 delay_to=200 min_frame_size=10 max_frame_size=5120 frame_error_rate=0.1 randseed=123 verbose Abb. 3: Starten des Distribution Systems



Entwickler instanziieren im eigenen Programm ein Objekt der Klasse NetworkCard. Damit können Rahmen versendet werden (Abb. 4).

NetworkCard nwcard=new NetworkCard(); byte[] frame=new byte[...]; // frame mit Daten füllen nwcard.send(frame); Abb. 4: Senden von Rahmen



Zum Empfang muss der Entwickler ein Objekt des Typs Receiver bei der Netzwerkkarte registrieren (Abb. 5). Die Registrierung erfolgt einmalig bei der Instanziierung der Netzwerkkarte.

public class TestReceiver implements Receiver { public void receive(byte[] frame) { // frame verarbeiten } } ... NetworkCard nwcard=new NetworkCard(new TestReceiver()); Abb. 5: Empfangen von Rahmen





Rahmen haben aus Sicht der Umgebung keine Struktur, sondern sind nur byte-Arrays. Bei einer fehlerfreien Übertragung stimmen sowohl Inhalt als auch die Länge. Simulierte Fehler können sich auf beides auswirken – es können sogar komplette Rahmen verschwinden. Das Distribution System arbeitet wie ein Bus (mit Broadcast). Sendet ein Sender, empfangen alle anderen (außer dem Sender) diesen Rahmen. Für unseren Fall mit zwei Teilnehmern ist das jedoch nicht von Belang.

Die Kommandozeilen-Parameter des DistributionSystems zeigt Abb. 6.

7

java rn.DistributionSystem delay_from: minimal delay per frame in ms (default=0) delay_to: maximal delay per frame in ms (default=2000) Note: a random uniform distribution between delay_from and delay_to is used to delay each frame min_frame_size: minimal allowed frame size (default=10) max_frame_size: maximal allowed frame size (default=5120) Note: frames of illegal lengths are dropped by the distribution system without any notification to the sender frame_error_rate: ratio of frames that have random errors, e.g. 0.001 means every 1000th frame (on average) has an error (default=0.001) randseed: seed for random number generator of frame errors port: server port number for internal communication (default 17381) verbose: print every event Abb. 6: Aufrufparameter des Distribution-Systems

6. Was ist abzuliefern Geben Sie zwei Klassen RNSender.java und RNReceiver.java ab, wenn notwendig, weitere Hilfsklassen. Darüber hinaus frame.txt (die Rahmendokumentation). Die Klassen sind schon übersetzt (d.h. es gibt schon die .class-Dateien). Berücksichtigen Sie exakt die Schreibweise der Klassen inkl. Groß/Kleinschreibung. Keine der Klassen liegt in Packages. Sender und Empfänger müssen über die Kommandozeile java -cp .;rn.jar RNSender bzw. java -cp .;rn.jar RNReceiver gestartet werden können. Es dürfen keine Kommandozeilenparameter erwartet werden. Darüber hinaus müssen die .java-Dateien über javac -cp .;rn.jar *.java bei installiertem Java Development Kit (JDK) ohne weitere Kommandozeilen-Parameter neu übersetzt werden können. Das bedeutet insbesondere, dass keine Bezeichner mit deutschen Umlauten verwendet werden dürfen. Die Quellen sollen mit dem JDK Standard Edition 7 oder 8 übersetzbar und ausführbar sein. Zusätzlich geben Sie zwei Dateien sender.out und receiver.out ab. Diese enthalten die Konsolen-Ausgaben beider Programme bei der Übertragung der Textdatei input.txt. Die Dateien wurden dabei direkt durch die Ausgabeumleitung erzeugt, also durch

8

java -cp .;rn.jar RNSender > sender.out java -cp .;rn.jar RNReceiver > receiver.out Bitte geben Sie all diese Dateien als ein ZIP-Archiv ab. Ein Rar, oder 7z oder ähnliches ist übrigens kein ZIP-Archiv. Es werden nur ZIP-Archive akzeptiert. Sowohl die .java- als auch die .class-Dateien sowie frame.txt liegen auf oberster Ebene der Verzeichnisstruktur. Es darf keine Verzeichnisse im ZIP-Archiv geben.

Achtung: Es ist keine Gruppenarbeit zugelassen. Jeder Teilnehmer muss sein eigenes Programm abliefern. Bitte geben Sie auch nicht ihre fertige Lösung "zur Information" an andere Teilnehmer weiter. Die Abgabe ist fällig am 4.12.2015 (harter Termin). Bitte per Email einsenden an [email protected].

7. Weitere Hinweise 7.1 Hilfsklassen, Bibliotheken, Annotations Java bietet eine Fülle von Container-Klassen an, z.B. ArrayList oder HashMap. Es ist nicht notwendig oder erwünscht, dass Sie solche Strukturen neu implementieren und damit das Rad neu erfinden. Es ist nicht erlaubt, weitere Bibliotheken außer  den Standardbibliotheken von Java Standard Edition sowie  der Simulationsumgebung rn.jar zu verwenden. Es ist nicht erlaubt, sich Hilfsklassen "aus dem Internet" zu beschaffen. Verwenden Sie außer der Java-Annotation @Override keine weiteren Annotationen, insb. keine selbst-definierten.

7.2. Kodierung von Inhalten in Rahmen-Feldern In ihrem Rahmen werden neben dem Nutzdatenfeld einige Felder, insb. Zahlen-Felder vorkommen. Da die Nutzdaten in der konkreten Aufgabenstellung aus einem String gebildet werden, liegt es nahe, alle Felder als String aufzufassen und erst am Ende in ein byte[] zu konvertieren. Das soll aber nicht gemacht werden. Als Beispiel, wie es nicht gemacht werden soll. Die Text-Zeile sei in der Variablen text gespeichert und es gäbe eine Prüfsumme checksum sowie eine weitere Nummer nr: String text; int checksum; int nr; Dann könnte man auf die Idee kommen, den Rahmen wie folgt zu generieren: byte[] frame=(text+"/"+checksum+"/"+nr).getBytes("UTF-8");

9

Auf Empfängerseite könnte man mit String frameStr=new String(frame,"UTF-8"); das byte[] in einen String umformen und nach den "/" von hinten suchen. Danach könnte man die abgetrennten Zahlen in int zurückverwandeln. Das soll nicht so gemacht werden! Grund: Auf Schicht 2 die Nummern als Strings zu verwandeln ist eine denkbar schlechte Idee:  Das Datenvolumen innerhalb von Rahmen steigt an, da das ziffernweise speichern als Text viel mehr Bits erfordert. Als Beispiel: ein 4-Byte-Integer benötigt binär 32 Bits, als Text bis zu 80 Bits.  Intern rechnen Computer immer binär, d.h. auf Sender und Empfängerseite müssen die beiden Repräsentationsarten konvertiert werden.  Wir müssen bedenken: das Schicht-2-Protokoll wird in der Netzwerkkarte oder in Chipsätzen ausgeführt, die keine umfangreichen Software-Bibliotheken verwenden können (auch wenn wir das in der Aufgabenstellung andern handhaben). Was sollen Sie stattdessen tun: Speichern Sie alle Felder byteweise. Ein 4-Byte-Integer soll beispielsweise in vier aufeinanderfolgende Bytes zerlegt werden. Hierzu stehen in Java verschiedene Ausdrucksmöglichkeiten zur Verfügung: Schiebeoperationen:  x > n schiebt eine Zahl x bitweise n-mal nach rechts (arithmetisch, d.h. unter Beibehaltung des Vorzeichens)  x >>> n schiebt eine Zahl x bitweise n-mal nach rechts (logisch, d.h. es werden 0Bits links reingeschoben) Bit-Operationen:  x & m führt eine logische Und-Maskierung aus. x & 0xff gibt beispielsweise das letzte Byte einer Zahl x zurück.  x | y führt eine logische Oder-Maskierung aus. Die gesetzten Bits von x und y werden zusammengefasst.  x ^ m führt eine logische XOR-Operation aus. An den Stellen, an denen m eine 1 hat, werden bei x die Bits invertiert zurückgegeben.

Beispiele: Das Zweierkomplement eines 4-Byte-Integers i wird durch (i ^ 0xffffffff)+1 gebildet. Von einem 4-Byte-Integer i erhält man die vier einzelnen Bytes durch i >>> 24 (i >>> 16) & 0xff (i >>> 8) & 0xff i & 0xff 10

Aus vier einzelnen Bytes setzt man einen 4-Byte-Integer zusammen durch (b3