Wenn wir von Vernetzung sprechen, dürfen wir nicht umhin, das OSI-Modell zu erwähnen.

Im Hinblick auf dieses Modell interessiert uns heute vor allem die Transportschicht (4).

Auf dieser Ebene arbeiten wir mit Daten, die sich „von Punkt A nach Punkt B“ bewegen. Die Hauptaufgabe der Transportschicht besteht darin, sicherzustellen, dass eine Nachricht unter Beibehaltung der richtigen Reihenfolge an das Ziel übermittelt wird. Die beiden gängigsten Transportschichtprotokolle sind: TCP und UDP. Sie arbeiten konzeptionell auf unterschiedliche Weise, aber jede hat ihre eigenen Vorteile, die es ihnen ermöglichen, spezifische Probleme zu lösen.

Schauen wir uns zunächst an, wie TCP funktioniert.

TCP (Transmission Control Protocol) ist ein Netzwerkprotokoll, das sicherstellt, dass eine Verbindung zwischen Hosts hergestellt wird, bevor Daten ausgetauscht werden.

Dies ist ein sehr zuverlässiges Protokoll, da jedes Mal, wenn ein weiteres Datenpaket gesendet wird, überprüft werden muss, ob das vorherige Paket empfangen wurde.

Die übertragenen Pakete werden geordnet, und wenn es Probleme mit einem bestimmten Paket gibt (dh der Empfänger bestätigt nicht, dass das Paket angekommen ist), wird das Paket erneut gesendet. Dadurch ist die Übertragungsrate relativ gering, da mehr Zeit für die strenge Überwachung und die Sicherstellung der korrekten Reihenfolge benötigt wird.

Hier kommt sein „Bruder“, das UDP-Protokoll, ins Spiel. Im Gegensatz zu TCP kümmert sich UDP nicht wirklich um die Reihenfolge und den Status jedes Pakets. Es werden lediglich Daten ohne Zustellungsbestätigung versendet. Darüber hinaus stellt es keine Verbindung her und ist in keiner Weise von einem Verbindungsstatus abhängig.

Sein Zweck besteht lediglich darin, Daten an eine Adresse zu senden. Daraus ergibt sich der Hauptnachteil des Protokolls: die geringe Zuverlässigkeit, da es einfach zu Datenverlusten kommen kann. Darüber hinaus muss der Empfänger darauf vorbereitet sein, dass die Daten möglicherweise nicht in der richtigen Reihenfolge ankommen. Allerdings hat das Protokoll auch einen Vorteil, nämlich eine höhere Übertragungsrate, da das Protokoll auf das Senden von Daten beschränkt ist.

Auch bei der Übermittlung der Daten selbst gibt es Unterschiede. Bei TCP werden Daten gestreamt, was bedeutet, dass die Daten keine Grenzen haben. Bei UDP werden Daten als Datagramme übertragen und haben Grenzen, und der Empfänger prüft die Integrität der Daten, jedoch nur, wenn die Nachricht erfolgreich empfangen wurde.

Fassen wir zusammen:

TCP ist ein zuverlässiges und genaues Protokoll, das unseren Datenverlust verhindert. Eine Nachricht wird immer mit höchster Genauigkeit zugestellt oder überhaupt nicht zugestellt. Der Empfänger benötigt keine Logik zum Bestellen von Daten, da eingehende Daten bereits geordnet sind. UDP ist nicht so zuverlässig, aber es ist ein schnelleres Datenübertragungsprotokoll. Die sendenden und empfangenden Parteien benötigen zusätzliche Logik, um mit diesem Protokoll arbeiten zu können. Aber schauen wir uns die Funktionsweise am Beispiel eines über das Netzwerk gespielten Computerspiels oder Handyspiels an. Es ist uns möglicherweise egal, was vor 5 Sekunden hätte ankommen sollen, und wir können ein paar Pakete überspringen, wenn sie nicht rechtzeitig ankommen – das Spiel verzögert sich möglicherweise, aber Sie können trotzdem spielen!

Um mit über UDP übertragenen Datagrammen zu arbeiten, verwenden wir in Java Objekte der Klassen DatagramSocket und DatagramPacket .

Um Daten auszutauschen, erstellen Sender und Empfänger Datagram-Sockets, also Instanzen der DatagramSocket- Klasse. Die Klasse verfügt über mehrere Konstruktoren. Der Unterschied besteht darin, wo der erstellte Socket eine Verbindung herstellt:

DatagramSocket () Stellt eine Verbindung zu jedem verfügbaren Port auf dem lokalen Computer her
DatagramSocket (int port) Stellt eine Verbindung zum angegebenen Port auf dem lokalen Computer her
DatagramSocket(int port, InetAddress addr) Stellt eine Verbindung zum angegebenen Port an einer Adresse auf dem lokalen Computer her (addr)

Die Klasse enthält viele Methoden zum Zugriff auf und zur Verwaltung der Socket-Parameter (wir werden sie uns etwas später ansehen) sowie Methoden zum Empfangen und Senden von Datagrammen:

send(DatagramPacket-Paket) Sendet in Pakete verpackte Datagramme
empfangen (DatagramPacket-Paket) Empfängt in Paketen verpackte Datagramme

DatagramPacket ist eine Klasse, die ein Datagrammpaket darstellt. Datagrammpakete werden verwendet, um einen verbindungslosen Paketzustellungsdienst zu implementieren. Jede Nachricht wird ausschließlich auf der Grundlage der in diesem Paket enthaltenen Informationen von einem Computer zum anderen weitergeleitet. Mehrere Pakete, die von einem Computer an einen anderen gesendet werden, können unterschiedlich weitergeleitet werden und in beliebiger Reihenfolge ankommen. Die Zustellung von Paketen kann nicht garantiert werden.

Konstrukteure:

DatagramPacket(byte[] buf, int length) Erstellt ein DatagramPacket , um Pakete der Länge length zu akzeptieren .
DatagramPacket(byte[] buf, int length, InetAddress-Adresse, int port) Erstellt ein Datagrammpaket, um Pakete der angegebenen Länge an die angegebene Portnummer auf dem angegebenen Host zu senden.
DatagramPacket(byte[] buf, int offset, int length) Erstellt ein DatagramPacket zum Akzeptieren von Paketen der Länge length und gibt dabei einen Offset im Puffer an.
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) Erstellt ein Datagrammpaket, um Pakete der Länge Länge mit Offset- Offset an die angegebene Portnummer auf dem angegebenen Host zu senden.
DatagramPacket(byte[] buf, int offset, int length, SocketAddress-Adresse) Erstellt ein Datagrammpaket, um Pakete der Länge Länge mit Offset- Offset an die angegebene Portnummer auf dem angegebenen Host zu senden.
DatagramPacket(byte[] buf, int length, SocketAddress-Adresse) Erstellt ein Datagrammpaket, um Pakete der angegebenen Länge an die angegebene Portnummer auf dem angegebenen Host zu senden.

Wir erinnern daran, dass der UDP-Ansatz keine Verbindung herstellt. Die Pakete werden in der Hoffnung verschickt, dass der Empfänger sie erwartet. Sie können jedoch eine Verbindung mithilfe der Methode connect(InetAddress addr, int port) der Klasse DatagramSocket herstellen .

Basierend auf einer Adresse und einem Port wird eine einseitige Verbindung mit dem Host hergestellt: entweder zum Senden oder Empfangen von Datagrammen. Die Verbindung kann mit der Methode „disconnect()“ beendet werden .

Versuchen wir, Servercode basierend auf DatagramSocket zu schreiben , um Daten zu empfangen:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

class Recipient {

   public static void main(String[] args) {
       try {
           DatagramSocket ds = new DatagramSocket(1050);

           while (true) {
               DatagramPacket pack = new DatagramPacket(new byte[5], 5);
               ds.receive(pack);
               System.out.println(new String(pack.getData()));
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

Wir erstellen ein DatagramSocket- Objekt, um Port 1050 abzuhören. Wenn es eine Nachricht empfängt, gibt es diese auf der Konsole aus. Wir werden das Wort „Hallo“ übertragen, daher begrenzen wir die Puffergröße auf fünf Bytes.

Jetzt erstellen wir die Senderklasse:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

class Sender {
   private String host;
   private int port;

   Sender(String host, int port) {
       this.host = host;
       this.port = port;
   }

   private void sendMessage(String mes) {
       try {
           byte[] data = mes.getBytes();
           InetAddress address = InetAddress.getByName(host);
           DatagramPacket pack = new DatagramPacket(data, data.length, address, port);
           DatagramSocket ds = new DatagramSocket();
           ds.send(pack);
           ds.close();
       } catch (IOException e) {
           System.err.println(e);
       }
   }

   public static void main(String[] args) {
   Sender sender = new Sender("localhost", 1050);
   String message = "Hello";

   Timer timer = new Timer();
   timer.scheduleAtFixedRate(new TimerTask() {
       @Override
       public void run() {
           sender.sendMessage(message);
       }
   }, 1000, 1000);
}

}

In der sendMessage- Methode erstellen wir ein DatagramPacket und einen DatagramSocket und senden unsere Nachricht. Beachten Sie, dass die Methode close() zum Schließen des DatagramSocket verwendet wird, nachdem die Nachricht gesendet wurde.

Jede Sekunde zeigt die Konsole des Empfängers die vom Absender gesendete eingehende „Hallo“-Nachricht an. Das bedeutet, dass unsere Kommunikation vollständig funktioniert.