I am currently working on an English translation. If you like to help to proofread please contact me: ullenboom ät g m a i l dot c o m.

Java Videotraining Werbung

1. Netzwerkprogrammierung

Zugriffe auf das Netzwerk sind heute so üblich wie der Zugriff auf das lokale Dateisystem. Seit Java 1.0 bietet Java eine Netzwerk-API zum Entwickeln von Client-Server-Anwendungen. Die Java-Bibliothek kann verschlüsselte Verbindungen aufbauen und bringt ebenso Unterstützung für das Hypertext Transfer Protocol (HTTP) mit. Bei den Aufgaben in diesem Kapitel geht es um das Beziehen von Ressourcen von einem Web-Server und das Entwickeln einer kleinen Client-Server-Anwendung mit eigenem Protokoll.

Voraussetzungen

  • URL-Klasse kennen

  • Datenströme beherrschen

  • Internetressourcen auslesen können

  • Client und Server mit Socket und ServerSocket implementieren können

Verwendete Datentypen in diesem Kapitel:

1.1. URL und URLConnection

In Java repräsentiert die naheliegende Klasse URL eine URL und URI eine URI. Über URL lässt sich eine HTTP-Verbindung öffnen, intern übernimmt das der Vermittler URLConnection.

Beide Klassen sind für moderne HTTP-Aufrufe nicht komfortabel; erst in Java 11 ist ein neues Paket java.net.http dazugekommen mit dem HttpClient im Mittelpunkt. Java-Enterprise-Frameworks haben weitere Lösungen, und im Open-Source-Universum gibt es ebenfalls viele Alternativen:

  • Client-API in Jakarta EE

  • WebClient in Spring Webflux

  • OkHttp

  • Apache HttpClient

  • Feign und Retrofit

1.1.1. Entfernte Bilder über die URL herunterladen ⭐

Die URL-Klasse bietet eine Methode, die einen InputStream liefert, sodass sich die Bytes der Ressource auslesen lassen.

Captain CiaoCiao entspannt sich gerne mit Bildern von wohl geformten Schiffen auf shiphub.com. Er hätte gerne einige Bilder auf seinem Speichermedium, sodass er auf langen Reisen etwas zum Träumen hat.

Aufgabe:

  • Schreibe ein Programm, das bei einer gegeben URL die Ressource herunterlädt und auf dem lokalen Dateisystem speichert.

  • Der Dateiname soll an die URL angelehnt sein.

1.1.2. Entfernte Textdatei über die URL einlesen ⭐

Das Center for Systems Science and Engineering (CSSE) der Johns Hopkins University veröffentlicht unter https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_daily_reports jeden Tag eine CSV-Datei mit Daten über Covid-Erkrankungen auf der ganzen Welt. Für den 1. März 2021 lautet die URL auf dem Server: https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/03-01-2021.csv.

Die Datei beginnt mit

FIPS,Admin2,Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,Combined_Key,Incidence_Rate,Case-Fatality_Ratio
,,,Afghanistan,2020-10-22 04:24:27,33.93911,67.709953,40510,1501,33824,5185,Afghanistan,104.06300129769207,3.7052579609972844
,,,Albania,2020-10-22 04:24:27,41.1533,20.1683,17948,462,10341,7145,Albania,623.6708596844812,2.574102964118565
,,,Algeria,2020-10-22 04:24:27,28.0339,1.6596,55081,1880,38482,14719,Algeria,125.60932701190256,3.4131551714747372

Aufgabe:

  • Lege eine neue Klasse CoronaData mit einer neuen Methode String findByDateAndSearchTerm(LocalDate date, String search) an.

    • findByDateAndSearchTerm(…​) soll mit dem Datum ein URL-Objekt aufbauen. Bedenke, dass der Dateiname die Reihenfolge Monat, Tag, Jahr hat.

    • Öffne einen Datenstrom zur generierten URL, lese den Strom zeilenweise ein, und filtere alle Zeilen heraus, die nicht den übergebenen Teil-String search beinhalten.

    • Am Ende entsteht ein String mit allen Zeilen mit dem Suchwort, ein CSV-Parser ist nicht nötig.

Beispiel:

  • Der Aufruf findByDateAndSearchTerm( LocalDate.now().minusDays( 1 ), "Miguel" ) liefert aus dem entfernten CSV-Dokument alle Corona-Zahlen von gestern, die "Miguel" enthalten.

  • Beispielhafte Rückgabe:

    8113,San Miguel,Colorado,US,2020-10-22 04:24:27,38.00450883,-108.4020725,100,0,0,100,"San Miguel, Colorado, US",1222.643354933366,0.0
    35047,San Miguel,New Mexico,US,2020-10-22 04:24:27,35.48014807,-104.8163562,151,0,0,151,"San Miguel, New Mexico, US",553.5799391428677,0.0

Wenn die CORONA-Zahlen zurückgehen es ist gut möglich, dass die CSSE keine neuen Dokumente mehr veröffentlicht. Die alten Dokumente sollten bleiben.

1.2. HTTP-Client

Zwar lässt sich mit URLConnection prinzipiell eine HTTP-Anfrage stellen, die HTTP-Methode (GET, POST, PUT …​) ändern und Header setzen, doch komfortabel ist das nicht. Daher wurde in Java 11 der HTTP-Client hinzugefügt. Mit der neuen API lassen sich HTTP-Ressourcen eleganter über das Netzwerk anfordern. Außerdem unterstützt der HTTP-Client HTTP/1.1 und HTTP/2 sowie synchrone als auch asynchrone Programmiermodelle. Mit dieser API wollen wir die Aufgaben lösen; wer kein Java 11 nutzen kann, findet mit dem https://github.com/AsyncHttpClient/async-http-client eine ähnliche Bibliothek für Java 8. Auch im nächsten Kapitel zu den Dateiformaten kommen wir in den Aufgaben noch einmal auf den HTTP-Client zurück, da oftmals JSON oder XML ausgetauscht werden.

1.2.1. Top-News von Hacker News ⭐⭐

Hacker News ist eine Website mit aktuell diskutieren Technologietrends. Die Beiträge lassen sich über einen Webservice abrufen, die Dokumentation findet sich unter https://github.com/HackerNews/API. Zwei Endpunkte sind:

Aufgabe:

  • Lege eine neue Klasse HackerNews an.

  • Implementiere eine neue Methode long[] hackerNewsTopStories(), die

  • Implementiere eine neue Methode String news(long id), die als String das komplette JSON-Dokument liefert.

Beispiel:

  • Die Klasse mit den beiden Methoden kann so genutzt werden:

    System.out.println( Arrays.toString( hackerNewsTopStories() ) );
    String newsInJson = news( 24857356 );
    System.out.println( newsInJson );

1.3. Socket und ServerSocket

Betriebssysteme bieten Sockets zur TCP/UDP-Kommunikation; sie kann Java über die Klassen

  • java.net.Socket und java.net.ServerSocket für TCP und

  • java.net.DatagramSocket für UDP nutzen.

Erzeugt werden Objekte von Socket und ServerSocket über den Konstruktor oder noch besser über die Fabriken javax.net.SocketFactory und javax.net.ServerSocketFactory; für UDP gibt es keine Fabrik.

1.3.1. Einen Schimpfserver implementieren und den Client dazu ⭐⭐

Bonny Brain nimmt bald am nächsten Schimpf-Wettbewerb teil. Sie möchte sich perfekt vorbereiten. Auf einem Server soll eine Anwendung laufen, die Sprüche verwaltet und Clients können sich zu diesem Schimpfserver verbinden und nach Sprüchen suchen.

Aufgabe:

  • Schreibe einen Server und Client.

  • Passe den Server so an, dass er mehrere Verbindungen annehmen kann; greife auf einen Thread-Pool zurück.

  • Der Thread-Pool soll eine maximale Anzahl von Threads nutzen, um Denial-of-Service-Attacken (DOS) einzudämmen. Ist die maximale Anzahl gleichzeitiger Verbindungen erschöpft, muss ein Client warten, bis wieder eine Verbindung frei wird.

Beispiel:

  • Nach dem Starten des Servers und des Clients kann eine Interaktion so aussehen:

    sir
    You, sir, are an oxygen thief!
    an
    You, sir, are an oxygen thief!
    Stop trying to be a smart ass, you're just an ass.

1.3.2. Einen Port-Scanner implementieren ⭐⭐

Bonny Brain installiert das neue Ay! OS, doch wichtige Analysewerkzeuge fehlen. Sie benötigt ein Werkzeug, das die belegten TCP/UDP-Ports erkennt und meldet.

Aufgabe:

  • Schreibe ein Programm, das versucht, auf allen TCP/UDP-Ports von 0 bis 49151 einen ServerSocket und DatagramSocket zu registrieren; wenn das gelingt, ist der Port frei, andernfalls besetzt.

  • Zeige die belegten Ports auf der Konsole an und dazu für die bekannten Ports eine Beschreibung des üblichen Dienstes, der diesen Port einnimmt.

Beispiel:

  • Die Ausgabe könnte so aussehen:

    Protocol   Port       Service
    TCP         135       EPMAP
    UPD         137       NetBIOS Name Service
    UPD         138       NetBIOS Datagram Service
    TCP         139       NetBIOS Session Service
    TCP         445       Microsoft-DS Active Directory
    TCP         843       Adobe Flash
    UPD        1900       Simple Service Discovery Protocol (SSDP)
    UPD        3702       Web Services Dynamic Discovery
    TCP        5040      
    UPD        5050      
    UPD        5353       Multicast DNS
    UPD        5355       Link-Local Multicast Name Resolution (LLMNR)
    TCP        5939      
    TCP        6463      
    TCP        6942      
    TCP       17500       Dropbox
    UPD       17500       Dropbox
    TCP       17600      
    TCP       27017       MongoDB
    UPD       42420      

Eine Netzwerkschnittstelle (engl. network interface) verbindet Computer über ein Rechnernetz. Im Folgenden gehen wir immer von einer TCP/UDP Schnittstelle aus. Die Netzwerkschnittstelle muss nicht physikalisch sein, sondern kann auch in Software implementiert werden. Wie die Loopback-Schnittstelle mit der IP 127.0.0.1 (IPv4) oder ::1 (IPv6). Typische Netzwerkschnittstellen existieren weiterhin für das LAN oder WLAN. Betriebssystem-Werkzeuge können alle Netzwerkschnittstellen anzeigen, etwa ipconfig /all unter Windows oder ip a unter Linux. In Java lassen sich die Netzwerkschnittstellen über java.net.NetworkInterface erfragen. Jede Netzwerkschnittstelle hat eine eigene IP-Adresse.

Zur Registrierung eines Server-Sockets gibt es zwei Möglichkeiten: entweder akzeptiert der Dienst nur Anfragen von einer speziellen lokalen InetAddress oder der Dienst akzeptiert von allen lokalen Adressen die Anfrage. Prinzipiell ist es also möglich, auf einer Netzwerkkarte den gleichen Port mehrmals zu binden, denn auf einer Netzwerkkarte lassen sich beliebig viele Netzwerkschnittstellen konfigurieren, denn sind haben unterscheidbare IP-Adressen.

Die Lösung der Aufgabe kann einen einfachen Test durchführen und den Socket-Socket auf allen Netzwerkschnittstellen registrieren; scheitert das, war auf einer der Netzwerkschnittstellen schon ein Dienst aktiv. Das reicht für uns als Kriterium, dass auf irgendeiner Netzwerkschnittstelle der Port belegt ist.