Netzwerkprogrammierung mit Java

Das Schulnetz beruht ebenso wie das Internet auf der Tatsache, dass Informationen zwischen Rechnern in einem Netzwerk ausgetauscht werden können.

In diesem Abschnitt werden wir den Austausch von Daten zwischen unterschiedlichen Computern durch verschiedene Java-Programme erläutern.

== Netzwerkprogrammierung mit Java Das Schulnetz beruht ebenso wie das Internet auf der Tatsache, dass Informationen zwischen Rechnern in einem Netzwerk ausgetauscht werden können. In diesem Abschnitt werden wir den Austausch von Daten zwischen unterschiedlichen Computern durch verschiedene Java-Programme erläutern.

DNS und IP-Adresse

Ein Rechner in einem Netzwerk ist über seine IP-Adresse (IP steht für Internet Protocol) eindeutig auffindbar. Eine solche IP-Adresse hat beispielsweise die Form 192.168.0.2. Auch Rechner im Internet besitzen jeweils eine IP-Adresse, über die sie von außen erreichbar sind.

Für Menschen ist es allerdings schwierig, sich Zahlen als Adressen für Webseiten zu merken. Deshalb gibt es den sogenannten Domain Name Service (im Deutschen häufig übersetzt mit "Namensauflösung"), kurz DNS, der menschenlesbare Adressen in IP-Adressen umwandelt. Im Prinzip funktioniert das ähnlich wie ein Telefonbuch: zu jedem Namen einer Webseite ist die zugehörige IP gespeichert.

Wenn du die IP-Adresse einer Seite herausfinden möchtest, kannst du beispielsweise auf dieser Seite die Webadresse eines Hosts eingeben, woraufhin die zugehörige IP-Nummer dieses Hosts erscheint.

Ist die IP-Adresse eines Rechners erst bekannt, so muss noch die Route herausgefunden werden, über die eine Verbindung zwischen meinem Rechner und dem entfernten Rechner hergestellt werden kann. Mehr Informationen darüber, wie das funktioniert, findest du z. B. hier.

=== DNS und IP-Adresse Ein Rechner in einem Netzwerk ist über seine _IP-Adresse_ (_IP_ steht für *Internet Protocol*) eindeutig auffindbar. Eine solche IP-Adresse hat beispielsweise die Form 192.168.0.2. Auch Rechner im Internet besitzen jeweils eine IP-Adresse, über die sie von außen erreichbar sind. Für Menschen ist es allerdings schwierig, sich Zahlen als Adressen für Webseiten zu merken. Deshalb gibt es den sogenannten _Domain Name Service_ (im Deutschen häufig übersetzt mit "Namensauflösung"), kurz _DNS_, der menschenlesbare Adressen in IP-Adressen umwandelt. Im Prinzip funktioniert das ähnlich wie ein Telefonbuch: zu jedem Namen einer Webseite ist die zugehörige IP gespeichert. Wenn du die IP-Adresse einer Seite herausfinden möchtest, kannst du beispielsweise auf http://ping.eu/nslookup/[icon:external-link[]dieser Seite] die Webadresse eines Hosts eingeben, woraufhin die zugehörige IP-Nummer dieses Hosts erscheint. Ist die IP-Adresse eines Rechners erst bekannt, so muss noch die _Route_ herausgefunden werden, über die eine Verbindung zwischen meinem Rechner und dem entfernten Rechner hergestellt werden kann. Mehr Informationen darüber, wie das funktioniert, findest du z. B. https://www.elektronik-kompendium.de/sites/net/0903151.html[icon:external-link[]hier].

Port

Zusätzlich zur IP-Adresse des Rechners muss man im Normalfall noch den Port angeben. An jedem Port kann ein Server auf eingehende Anfragen warten. Für normale Internetseiten wird normalerweise Port 80 verwendet.

Somit entspricht die IP-Adresse gewissermaßen der Adresse eines Geschäftshauses, der Port der Türe, wo sich eine bestimmter Service befindet.

=== Port Zusätzlich zur IP-Adresse des Rechners muss man im Normalfall noch den _Port_ angeben. An jedem Port kann ein _Server_ auf eingehende Anfragen warten. Für normale Internetseiten wird normalerweise Port 80 verwendet. Somit entspricht die IP-Adresse gewissermaßen der Adresse eines Geschäftshauses, der Port der Türe, wo sich eine bestimmter Service befindet.

URL (Uniform Resource Locator)

=== URL (Uniform Resource Locator)

Um auf eine Webseite zugreifen zu können, muss man die Adresse nach einem gewissen Schema eingeben. Für eine Internetseite sieht diese beispielsweise so aus:

Um auf eine Webseite zugreifen zu können, muss man die Adresse nach einem gewissen Schema eingeben. Für eine Internetseite sieht diese beispielsweise so aus:
http://www.meine-seite.de:8080/kapitel1/index.html
---- http://www.meine-seite.de:8080/kapitel1/index.html ----
  • http ist das Protokoll, legt also das Austauschformat fest, in dem die angeforderten Informationen ausgetauscht werden.

  • www.meine-seite.de ist der Host, auf dem die angeforderte Seite zu finden ist.

  • 8080 ist der Port, der durch einen Doppelpunkt vom Host getrennt ist. Im Falle einer gewöhnlichen Webseite ist der Port 80 und muss nicht gesondert eingegeben werden.

  • kapitel1/index.html ist der Pfad zu der von uns angeforderten Seite auf dem Host.

* `http` ist das _Protokoll_, legt also das Austauschformat fest, in dem die angeforderten Informationen ausgetauscht werden. * `www.meine-seite.de` ist der _Host_, auf dem die angeforderte Seite zu finden ist. * `8080` ist der _Port_, der durch einen Doppelpunkt vom Host getrennt ist. Im Falle einer gewöhnlichen Webseite ist der Port 80 und muss nicht gesondert eingegeben werden. * `kapitel1/index.html` ist der _Pfad_ zu der von uns angeforderten Seite auf dem Host.

Weitere Beispiele zu URLs findest du hier auf Wikipedia.

Weitere Beispiele zu URLs findest du https://de.wikipedia.org/wiki/Uniform_Resource_Locator[icon:external-link[]hier auf Wikipedia].

Erstellen eines Clients in Java

== Erstellen eines Clients in Java

Verbinden mit einem Server

Nachdem nun klar sein sollte, wie man Ressourcen im Internet mit einer eindeutigen "Adresse" versieht, betrachten wir im Folgenden die Umsetzung einer Netzwerkverbindung mit Java.

Möchte man sich beispielsweise mit dem Server mit dem Namen www.meine-seite.de an Port 8080 verbinden, so gelingt dies in Java mittels

=== Verbinden mit einem Server Nachdem nun klar sein sollte, wie man Ressourcen im Internet mit einer eindeutigen "Adresse" versieht, betrachten wir im Folgenden die Umsetzung einer Netzwerkverbindung mit Java. Möchte man sich beispielsweise mit dem Server mit dem Namen `www.meine-seite.de` an Port `8080` verbinden, so gelingt dies in Java mittels
Socket socket=new Socket(java.net.InetAddress.getByName("www.meine-seite.de"),8080);
BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
OutputStream out=socket.getOutputStream();
Java
[source,java,indent=0] ---- Socket socket=new Socket(java.net.InetAddress.getByName("www.meine-seite.de"),8080); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); OutputStream out=socket.getOutputStream(); ----

Die erste Zeile erzeugt einen socket, also einen Kontaktpunkt für unsere Verbindung. Daraus können ein lesender (in) und ein schreibender Kanal (out) erstellt werden.

Die erste Zeile erzeugt einen _socket_, also einen Kontaktpunkt für unsere Verbindung. Daraus können ein lesender (`in`) und ein schreibender Kanal (`out`) erstellt werden.

Beachte, dass die Anweisung getByName("www.meine-seite.de") von sich aus die IP-Adresse nachschlägt und auch eine Route zum entsprechenden Host sucht.

Als Programmierer muss man sich um nichts weiter kümmern!

[NOTE] ==== Beachte, dass die Anweisung `getByName("www.meine-seite.de")` von sich aus die IP-Adresse nachschlägt und auch eine Route zum entsprechenden Host sucht. Als Programmierer muss man sich um nichts weiter kümmern! ====

Lesen und Schreiben

In unserem Fall wurden ein lesender und ein schreibender Kanal erstellt, die folgendermaßen verwendet werden können:

=== Lesen und Schreiben In unserem Fall wurden ein lesender und ein schreibender Kanal erstellt, die folgendermaßen verwendet werden können:
eingabe=in.readLine();
System.out.println(eingabe);
out.write(eingabe.getBytes());
out.write('\n');
Java
[source, java, indent=0] ---- eingabe=in.readLine(); System.out.println(eingabe); out.write(eingabe.getBytes()); out.write('\n'); ----

Zunächst wird eine Nachricht vom Server mittels eingabe=in.readLine() eingelesen (bis zum Ende einer Zeile) und daraufhin ausgegeben.

Im Anschluss daran wird in diesem Fall der erhaltene Text noch an den Server zurückgesendet und mit einem Zeilenvorschub (\n) beendet.

Zunächst wird eine Nachricht vom Server mittels `eingabe=in.readLine()` eingelesen (bis zum Ende einer Zeile) und daraufhin ausgegeben. Im Anschluss daran wird in diesem Fall der erhaltene Text noch an den Server zurückgesendet und mit einem Zeilenvorschub (`\n`) beendet.

Trennen der Verbindung

Die Verbindung vom Client zum Server kann durch die folgenden Zeilen beendet werden:

=== Trennen der Verbindung Die Verbindung vom Client zum Server kann durch die folgenden Zeilen beendet werden:
in.close();
out.close();
socket.close();
Java
[source,java,indent=0] ---- in.close(); out.close(); socket.close(); ----

Den gesamten Code des Clients kann man hier einsehen.

Den gesamten Code des Clients kann man <<tags_b,hier>> einsehen.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class Client {

    public static void main(String[] args) {

        String eingabe="";

        try{
        	Socket socket=new Socket(java.net.InetAddress.getByName("localhost"),6000);
        	BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        	BufferedReader eingabeKonsole=new BufferedReader(new InputStreamReader(System.in));
        	OutputStream out=socket.getOutputStream();

        	eingabe=in.readLine();
        	System.out.println(eingabe);

        	eingabe=eingabeKonsole.readLine();
        	out.write(eingabe.getBytes());
        	out.write('\n');

        	eingabe=in.readLine();
        	System.out.println(eingabe);

        	System.out.println("");

        	in.close();
        	out.close();
        	socket.close();
                System.out.println("Client erfolgreich beendet ...");
        } catch (Exception e){
        	System.out.println("Fehler aufgetreten: "+e.toString());
        	System.exit(1);
        }

    }
}
Java
[source,java,indent=0] ---- import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class Client { public static void main(String[] args) { String eingabe=""; try{ Socket socket=new Socket(java.net.InetAddress.getByName("localhost"),6000); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader eingabeKonsole=new BufferedReader(new InputStreamReader(System.in)); OutputStream out=socket.getOutputStream(); eingabe=in.readLine(); System.out.println(eingabe); eingabe=eingabeKonsole.readLine(); out.write(eingabe.getBytes()); out.write('\n'); eingabe=in.readLine(); System.out.println(eingabe); System.out.println(""); in.close(); out.close(); socket.close(); System.out.println("Client erfolgreich beendet ..."); } catch (Exception e){ System.out.println("Fehler aufgetreten: "+e.toString()); System.exit(1); } } } ----

Erstellen eines Servers in Java

== Erstellen eines Servers in Java

Auch das Erstellen eines Servers gelingt in Java recht einfach: man muss nur angeben, an welchem Port der Server zur Verfügung steht und, sobald eine Verbindungsanfrage hereinkommt, diese bearbeiten:

Auch das Erstellen eines Servers gelingt in Java recht einfach: man muss nur angeben, an welchem Port der Server zur Verfügung steht und, sobald eine Verbindungsanfrage hereinkommt, diese bearbeiten:
try {
    System.out.println("Warte auf Port 6000 auf Clients");
    ServerSocket simpleserver = new ServerSocket(6000);

    while (true) {
        Socket simplesocket = simpleserver.accept();
        (new server_thread(++counter, simplesocket)).start();
    }
} catch (IOException e) {
    System.out.println(e.toString());
}
Java
[source, java, indent=0] ---- try { System.out.println("Warte auf Port 6000 auf Clients"); ServerSocket simpleserver = new ServerSocket(6000); while (true) { Socket simplesocket = simpleserver.accept(); (new server_thread(++counter, simplesocket)).start(); } } catch (IOException e) { System.out.println(e.toString()); } ----

In diesem Beispiel wartet der Server an Port 6000 und sobald eine Anfrage hereinkommt, wird diese akzeptiert mittels simpleserver.accept(). Man erhält dabei einen Endpunkt der Verbindung den man in diesem Fall an einen neuen Thread weitergibt, damit der Server für die nächste Anfrage sofort wieder zur Verfügung steht.

In diesem Beispiel wartet der Server an Port 6000 und sobald eine Anfrage hereinkommt, wird diese akzeptiert mittels `simpleserver.accept()`. Man erhält dabei einen Endpunkt der Verbindung den man in diesem Fall an einen neuen Thread weitergibt, damit der Server für die nächste Anfrage sofort wieder zur Verfügung steht.

Lesen und Schreiben auf Server-Seite

Auch der Server benötigt für seine Kommunikation einen Kanal zum Lesen und einen weiteren zum Schreiben. Diese bezieht man im Thread mittels

=== Lesen und Schreiben auf Server-Seite Auch der Server benötigt für seine Kommunikation einen Kanal zum Lesen und einen weiteren zum Schreiben. Diese bezieht man im Thread mittels
OutputStream out = simplesocket.getOutputStream();
BufferedReader in_socket = new BufferedReader(new InputStreamReader(simplesocket.getInputStream()));
Java
[source, java, indent=0] ---- OutputStream out = simplesocket.getOutputStream(); BufferedReader in_socket = new BufferedReader(new InputStreamReader(simplesocket.getInputStream())); ----

Mit in werden hereinkommende Nachrichten empfangen, über out werden Nachrichten nach außen geschickt:

Mit `in` werden hereinkommende Nachrichten empfangen, über `out` werden Nachrichten nach außen geschickt:
eingabe = in_socket.readLine();
out.write(text.getBytes());
Java
[source,java, indent=0] ---- eingabe = in_socket.readLine(); out.write(text.getBytes()); ----

Schließen der Verbindung von Server-Seite aus

Am Ende müssen alle Kanäle und der Socket wieder geschlossen werden:

=== Schließen der Verbindung von Server-Seite aus Am Ende müssen alle Kanäle und der Socket wieder geschlossen werden:
out.close();
in_socket.close();
simplesocket.close();
Java
[source,java,indent=0] ---- out.close(); in_socket.close(); simplesocket.close(); ----

Somit ist die Verbindung getrennt.

Den Code für einen kompletten Server findest du hier.

Somit ist die Verbindung getrennt. Den Code für einen kompletten Server findest du <<tags_c,hier>>.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Server {

    public static void main(String[] args) {
        int counter = 0;
        try {
            System.out.println("IP-Adresse: " + Inet4Address.getLocalHost().getHostAddress());
        } catch (UnknownHostException ex) {

        }
        try {
            System.out.println("Warte auf Port 6000 auf Clients");
            ServerSocket simpleserver = new ServerSocket(6000);

            while (true) {
                Socket simplesocket = simpleserver.accept();
                (new server_thread(++counter, simplesocket)).start();
            }
        } catch (IOException e) {
            System.out.println(e.toString());
        }
    }
}

class server_thread extends Thread {

    private byte name;
    private String text, eingabe;
    private Socket simplesocket;

    public server_thread(int name, Socket simplesocket) {
        this.name = (byte) name;
        this.simplesocket = simplesocket;
    }

    public void run() {
        try {

            System.out.println("\nHabe Verbindung zu Client " + name + " hergestellt!");
            OutputStream out = simplesocket.getOutputStream();
            BufferedReader in_socket = new BufferedReader(new InputStreamReader(simplesocket.getInputStream()));

            text = "Hallo Client Nr. " + name + ": Gib ein Wort ein\n";
            out.write(text.getBytes());
            eingabe = in_socket.readLine();

            text = "Hallo Client Nr. " + name + ": Dein Wort war " + eingabe + ". Und tschüss!\n";
            out.write(text.getBytes());

            System.out.println("\nDas Wort von Client " + name + " war " + eingabe);

            out.close();
            in_socket.close();
            simplesocket.close();
        } catch (IOException e) {
            System.out.println("Fehler aufgetreten..." + e.toString());
            System.exit(1);
        }

        System.out.println("\nVerbindung mit Client " + name + " beendet!");
    }
}
Java
[source,java,indent=0] ---- import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.logging.Level; import java.util.logging.Logger; public class Server { public static void main(String[] args) { int counter = 0; try { System.out.println("IP-Adresse: " + Inet4Address.getLocalHost().getHostAddress()); } catch (UnknownHostException ex) { } try { System.out.println("Warte auf Port 6000 auf Clients"); ServerSocket simpleserver = new ServerSocket(6000); while (true) { Socket simplesocket = simpleserver.accept(); (new server_thread(++counter, simplesocket)).start(); } } catch (IOException e) { System.out.println(e.toString()); } } } class server_thread extends Thread { private byte name; private String text, eingabe; private Socket simplesocket; public server_thread(int name, Socket simplesocket) { this.name = (byte) name; this.simplesocket = simplesocket; } public void run() { try { System.out.println("\nHabe Verbindung zu Client " + name + " hergestellt!"); OutputStream out = simplesocket.getOutputStream(); BufferedReader in_socket = new BufferedReader(new InputStreamReader(simplesocket.getInputStream())); text = "Hallo Client Nr. " + name + ": Gib ein Wort ein\n"; out.write(text.getBytes()); eingabe = in_socket.readLine(); text = "Hallo Client Nr. " + name + ": Dein Wort war " + eingabe + ". Und tschüss!\n"; out.write(text.getBytes()); System.out.println("\nDas Wort von Client " + name + " war " + eingabe); out.close(); in_socket.close(); simplesocket.close(); } catch (IOException e) { System.out.println("Fehler aufgetreten..." + e.toString()); System.exit(1); } System.out.println("\nVerbindung mit Client " + name + " beendet!"); } } ----

Das HTTP-Protokoll

Für das Anfragen und Übertragen von Internet-Ressourcen wird das HTTP-Protokoll verwendet (Hypertext Transfer Protocol), so auch bei der Nutzung des Internets über einen Browser.

So schickt der Client nach der Verbindung mit dem Server diesem die Anfrage

== Das HTTP-Protokoll Für das Anfragen und Übertragen von Internet-Ressourcen wird das _HTTP-Protokoll_ verwendet (*Hypertext Transfer Protocol*), so auch bei der Nutzung des Internets über einen Browser. So schickt der Client nach der Verbindung mit dem Server diesem die Anfrage

Anfrage vom Client an den Server

=== Anfrage vom Client an den Server
GET kapitel1/index.html HTTP/1.1
Host: www.meine-seite.de
---- GET kapitel1/index.html HTTP/1.1 Host: www.meine-seite.de ----

Wenn diese Seite existiert, antwortet der Server beispielsweise mit

Wenn diese Seite existiert, antwortet der Server beispielsweise mit

Antwort des Servers

=== Antwort des Servers
HTTP/1.1 200 OK
Content-Length: 5
Connection: close
Content-Type: text/html

Hallo
---- HTTP/1.1 200 OK Content-Length: 5 Connection: close Content-Type: text/html Hallo ----

Der Status-Code 200 bedeutet, dass die angeforderte Ressource gefunden werden konnte und der Server liefert diese mit einigen Zusatzinformationen zurück:

  • Content-Length gibt an, wie lang der Rumpf der Nachricht ist (hier das "Hallo").

  • Connection: close gibt an, dass nach dieser Übermittlung vom Server die Verbindung beendet wird.

  • Content-Type: text/html gibt an, dass der folgende Rumpf der Nachricht als HTML zu interpretieren ist. Diese Information ist wichtig für den Browser zur korrekten Darstellung.

Der _Status-Code_ 200 bedeutet, dass die angeforderte Ressource gefunden werden konnte und der Server liefert diese mit einigen Zusatzinformationen zurück: * `Content-Length` gibt an, wie lang der Rumpf der Nachricht ist (hier das "Hallo"). * `Connection: close` gibt an, dass nach dieser Übermittlung vom Server die Verbindung beendet wird. * `Content-Type: text/html` gibt an, dass der folgende Rumpf der Nachricht als HTML zu interpretieren ist. Diese Information ist wichtig für den Browser zur korrekten Darstellung.

Mehr zum HTTP-Protokoll findest du auf Wikipedia.

Mehr zum HTTP-Protokoll findest du auf https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol[icon:external-link[]Wikipedia].

Seit dem NSA-Skandal sind immer mehr Seiten dazu übergegangen, das für die Übertragung verschlüsselter Seiten bestehende HTTPS-Protokoll zu nutzen.

Somit funktionieren unsere Beispiele nur mit einem immer kleiner werdenden Teil aller Internetseiten.

[NOTE] ==== Seit dem NSA-Skandal sind immer mehr Seiten dazu übergegangen, das für die Übertragung verschlüsselter Seiten bestehende _HTTPS-Protokoll_ zu nutzen. Somit funktionieren unsere Beispiele nur mit einem immer kleiner werdenden Teil aller Internetseiten. ====

Programme zu diesem Kapitel

Zu diesem Kapitel stehen vier Programme zur Verfügung:

  • Server.java und Client.java stellen ein einfaches Chat-Programm zur Verfügung: erst den Server starten, dann den Client und der Eingabeaufforderung Folge leisten.

  • ClientWhile ermöglicht es, TCP-Anfragen an einen Server zu stellen (ähnlich wie telnet). Damit kann man beispielsweise eine Anfrage gemäß dem HTTP-Protokoll an einen Server im Internet stellen und die Antwort des Servers einsehen.

  • ServerHttp.java enthält einen rudimentären HTTP-Server, basierend auf Server.java. Sobald der Server gestartet ist, kann auf dem selben Rechner im Browser über die Eingabe von localhost:8080 die Rückgabe des Servers betrachtet werden.

Hier kannst du die vollständigen Programme zu diesem Kapitel herunterladen:

== Programme zu diesem Kapitel Zu diesem Kapitel stehen vier Programme zur Verfügung: * _Server.java_ und _Client.java_ stellen ein einfaches _Chat-Programm_ zur Verfügung: erst den Server starten, dann den Client und der Eingabeaufforderung Folge leisten. * _ClientWhile_ ermöglicht es, TCP-Anfragen an einen Server zu stellen (ähnlich wie telnet). Damit kann man beispielsweise eine Anfrage gemäß dem HTTP-Protokoll an einen Server im Internet stellen und die Antwort des Servers einsehen. * _ServerHttp.java_ enthält einen rudimentären HTTP-Server, basierend auf _Server.java_. Sobald der Server gestartet ist, kann auf dem selben Rechner im Browser über die Eingabe von `localhost:8080` die Rückgabe des Servers betrachtet werden. Hier kannst du die vollständigen Programme zu diesem Kapitel herunterladen:

Dateiname

ServerUndClient.zip

Größe in Bytes

29053

Einfügezeitpunkt

21.10.2017, 13:32:19

Wahlweise findest du die einzelnen Programme auch auf Github unter https://github.com/menzelths/ClientUndServer zum Analysieren oder Klonen.

Wahlweise findest du die einzelnen Programme auch auf Github unter https://github.com/menzelths/ClientUndServer zum Analysieren oder Klonen.

Zurück zur Hauptseite

<<tags_@,Zurück zur Hauptseite>>