1.0
Die Vermittlungsschicht (Internet Layer)
übernimmt das Routing
realisiert Ende-zu-Ende-Kommunikation
überträgt Pakete
ist im Internet durch IP realisiert
löst folgende Probleme:
Sender und Empfänger erhalten netzweit eindeutige Bezeichner (⇒ IP-Adressen)
die Pakete werden durch spezielle Geräte (⇒ Router) weitergeleitet
Transmission Control Protocol (TCP), RFC 793
verbindungsorientierte Kommunikation
ebenfalls Konzept der Ports
Verbindungsaufbau zwischen zwei Prozessen (Dreifacher Handshake, Full-Duplex-Kommunikation)
geordnete Kommunikation
zuverlässige Kommunikation
Flusskontrolle
hoher Overhead ⇒ eher langsam
nur Unicasts
User Datagram Protocol (UDP), RFC 768
verbindungslose Kommunikation
unzuverlässig ⇒ keine Fehlerkontrolle
ungeordnet ⇒ beliebige Reihenfolge
wenig Overhead ⇒ schnell
Größe der Nutzdaten ist 65507 Byte
Anwendungsfelder:
Anw. mit vorwiegend kurzen Nachrichten (z. B. NTP, RPC, NIS)
Anw. mit hohem Durchsatz, die gel. Fehler tolerieren (z. B. Multimedia)
Multicasts sowie Broadcasts
UDP nutzt für die Kommunikation "Datagramme" (Pakete). In der Praxis sin die Nutzdaten meist deutlich kleiner und orientieren sich an der Größe für IP-Pakete.
RFC 7230 – 7235: HTTP/1.1 (redigiert im Jahr 2014; urspr. 1999 RFC 2626)
RFC 7540: HTTP/2 (seit Mai 2015 standardisiert)
Eigenschaften:
Client / Server (Browser / Web-Server)
basierend auf TCP, i. d. R. Port 80
Server (meist) zustandslos
seit HTTP/1.1 auch persistente Verbindungen und Pipelining
abgesicherte Übertragung (Verschlüsselung) möglich mittels Secure Socket Layer (SSL) bzw. Transport Layer Security (TLS)
HTTP-Kommandos
(„Verben“)
HEAD
GET
POST
PUT
PATCH
DELETE
OPTIONS
TRACE
CONNECT
...
Aufbau der Dokumentenbezeichner Uniform Resource Locator (URL)
scheme://host[:port][abs_path[?query][#anchor]]
Protokoll (case-insensitive) (z. B. http, https oder ftp)
DNS-Name (oder IP-Adresse) des Servers (case-insensitive)
(optional) falls leer, 80 bei http und 443 bei https
(optional) Pfadausdruck relativ zum Server-Root (case-sensitive)
(optional) direkte Parameterübergabe (case-sensitive) (?from=…&to=…)
(optional) Sprungmarke innerhalb des Dokuments
Uniform Resource Identifier (URI) sind eine Verallgemeinerung von URLs.
definiert in RFC 1630 (im Jahr 1994)
entweder URL (Location) oder URN (Name) (z. B. urn:isbn:1234567890)
Beispiele von URIs, die keine URL sind, sind XML Namespace Iidentifiers
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">...</svg>
Dient dem Anfordern von HTML-Daten vom Server (Request-Methode).
Minimale Anfrage:
1GET <Path> HTTP/1.1
2Host: <Hostname>
3Connection: close
4<Leerzeile (CRLF)>
Client kann zusätzlich weitere Infos über die Anfrage sowie sich selbst senden.
Server sendet Status der Anfrage sowie Infos über sich selbst und ggf. die angeforderte HTML-Datei.
Fehlermeldungen werden ggf. vom Server ebenfalls als HTML-Daten verpackt und als Antwort gesendet.
Beispiel Anfrage des Clients
GET /web/web.php HTTP/1.1
Host: archive.org
**CRLF**
Beispiel Antwort des Servers
HTTP/1.1 200 OK
Server: nginx/1.25.1
Date: Thu, 22 Feb 2024 19:47:11 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
**CRLF**
<!DOCTYPE html>
…
</html>**CRLF**
Sockets sind Kommunikationsendpunkte.
Sockets werden adressiert über die IP-Adresse (InetAddress-Objekt) und eine interne Port-Nummer (int-Wert).
Sockets gibt es bei TCP und auch bei UDP, allerdings mit unterschiedlichen Eigenschaften:
verbindungsorientierte Kommunikation über Streams
verbindungslose Kommunikation mittels Datagrams
Das Empfangen von Daten ist in jedem Fall blockierend, d. h. der empfangende Thread bzw. Prozess wartet, falls keine Daten vorliegen.
Der Server-Prozess wartet an dem bekannten Server-Port.
Der Client-Prozess erzeugt einen privaten Socket.
Der Socket baut zum Server-Prozess eine Verbindung auf – falls der Server die Verbindung akzeptiert.
Die Kommunikation erfolgt Strom-orientiert: Für beide Parteien wird je ein Eingabestrom und ein Ausgabestrom eingerichtet, über den nun Daten ausgetauscht werden können.
Wenn alle Daten ausgetauscht wurden, schließen im Allg. beide Parteien die Verbindung.
1import java.net.*;
2import java.io.*;
34
public class LowPortScanner {
5public static void main(String [] args) {
6String host = "localhost";
7if (args.length > 0) { host = args [0]; }
8for (int i = 1; i < 1024; i++) {
9try {
10Socket s = new Socket(host, i);
11System.out.println("There is a server on port "+ i + "at "+host);
12s.close();
13} catch (UnknownHostException e) {
14System.err.println(e);
15break ;
16}
17catch (IOException e) {/* probably no server waiting at this port */ }
18} } }
Nach erfolgtem Verbindungsaufbau können zwischen Client und Server mittels des Socket-InputStream und Socket-OutputStream Daten ausgetauscht werden.
Hierzu leitet man die rohen Daten am besten durch geeignete Filter-Streams, um eine möglichst hohe semantische Ebene zu erreichen.
Beispiele: PrintWriter, BufferedReader, BufferedInputStream, BufferedOutputStream
Die Netzwerkkommunikation kann dann ggf. bequem über wohlbekannte und komfortable Ein- und Ausgabe-Routinen (z. B. readLine oder println) durchgeführt werden.
Filter-Streams werden auch für den Zugriff auf andere Geräte und Dateien verwendet.
Durch die Verwendung des Decorater-Patterns können die Filter-Streams beliebig geschachtelt werden und vielfältig verwendet werden. Dies macht die Anwendungsprogrammierung einfacher und erlaubt zum Beispiel das einfache Umwandeln von Zeichenketten, Datenkomprimierung, Verschlüsselung, usw.
1import java.net.*; import java.io.*;
23
public class EchoClient {
4public static void main(String[] args) throws IOException {
5BufferedReader userIn = new BufferedReader(new InputStreamReader(System.in));
6while (true) {
7String theLine = userIn.readLine();
8if (theLine.equals(".")) break;
9try (Socket s = new Socket("localhost"/*hostname*/, 7/*serverPort*/)) {
10BufferedReader networkIn =
11new BufferedReader(new InputStreamReader(s.getInputStream()));
12PrintWriter networkOut = new PrintWriter(s.getOutputStream());
13networkOut.println(theLine);
14networkOut.flush();
15System.out.println(networkIn.readLine());
16} } } }
1import java.net.*; import java.io.*;
23
public class EchoServer {
4public static void main(String[] args) {
5BufferedReader in = null;
6try {
7ServerSocket server = new ServerSocket(7 /*DEFAULT PORT*/);
8while (true) {
9try (Socket con = server.accept()) {
10in = new BufferedReader(new InputStreamReader(con.getInputStream()));
11PrintWriter out = new PrintWriter(con.getOutputStream());
12out.println(in.readLine()); out.flush() ;
13} catch (IOException e) { System.err.println(e); }
14}
15} catch (IOException e) { System.err.println(e); }
16} }
Clientseitig
DatagramSocket erzeugen
DatagramPacket erzeugen
DatagramPacket absenden
ggf. Antwort empfangen und verarbeiten
Serverseitig
DatagramSocket auf festem Port erzeugen
Endlosschleife beginnen
DatagramPacket vorbereiten
DatagramPacket empfangen
DatagramPacket verarbeiten
ggf. Antwort erstellen und absenden
1import java.net.*; import java.io.*;
23
public class UDPEchoServer {
4public final static int DEFAULT_PORT = 7; // privileged port
5public static void main(String[] args) {
6try (DatagramSocket server = new DatagramSocket(DEFAULT_PORT)) {
7while(true) {
8try {
9byte[] buffer = new byte[65507]; // room for incoming message
10DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
11server.receive(dp) ;
12String data = new String(dp.getData(),0,dp.getLength());
13DatagramPacket dp2 =
14new DatagramPacket(data.getBytes(),
15data.getBytes().length, dp.getAddress(), dp.getPort());
16server.send(dp2) ;
17} catch (IOException e) {System.err.println(e);}
18} } } }
Ein einfacher HTTP-Client
Schreiben Sie einen HTTP-Client, der den Server www.michael-eichberg.de kontaktiert, die Datei /index.html anfordert und die Antwort des Servers auf dem Bildschirm ausgibt.
Verwenden Sie HTTP/1.1 und eine Struktur ähnlich dem in der Vorlesung vorgestellten Echo-Client.
Senden Sie das GET-Kommando, die Host-Zeile sowie eine Leerzeile als Strings an den Server.
Modifizieren Sie Ihren Client, so dass eine URL als Kommandozeilenparameter akzeptiert wird.
Verwenden Sie die (existierende) Klasse URL, um die angegebene URL zu zerlegen.
Modifizieren Sie Ihr Programm, so dass die Antwort des Servers als lokale Datei abgespeichert wird. Laden Sie die Datei zum Anzeigen in einen Browser.
Nutzen Sie die Klasse FileOutputStream oder FileWriter zum Speichern der Datei.
Kann Ihr Programm auch Bilddateien (z. B. "/exercises/star.jpg") korrekt speichern?
Protokollaggregation
Schreiben Sie ein UDP-basiertes Java-Programm, mit dem sich Protokoll-Meldungen auf einem Server zentral anzeigen lassen. Das Programm soll aus mehreren Clients und einem Server bestehen. Jeder Client liest von der Tastatur eine Eingabezeile in Form eines Strings ein, der dann sofort zum Server gesendet wird. Der Server wartet auf Port 4999 und empfängt die Meldungen beliebiger Clients, die er dann unmittelbar ausgibt.