Rechnernetze / Kommunikationssysteme

Praktikum UDP-Programmierung

In diesem Versuch soll die Nutzung des UDP-Sockets in Java erprobt werden. Dazu soll analog zu dem bekannten Ping-Befehl ein Client programmiert werden, welcher Requests an einen Server versendet welcher die Request wiederum zurücksendet. Da mittels Java nicht direkt ICMP-Requests gesendet werden können, soll in diesem Praktikum das UDP-Protokoll verwendet werden.
Der Server ist bereits fertig implementiert. Sie müssen lediglich den dazu passenden Client programmieren. Da UDP lediglich einen unzuverlässigen Transport ermöglicht, sind Vorkehrungen auf der Anwendungsschicht bezüglich Fehlern zu treffen.
Um im Labor eine reale Internetverbindung zu simulieren, werden die Pakete vom Server künstlich um einen zufälligen Wert verzögert (AVERAGE_DELAY) und teilweise verworfen (LOSS_RATE). Testen Sie Ihren Client auf Funktionsfähigkeit mit verschiedenen Werten!
Der Aufruf des Servers erfolgt mittels:

java PingServer Portnummer

wobei eine Portnummer größer als 1023 gewählt werden sollte, um ohne Administratorrechte auszukommen.

Teil 1: Vorlesungsbeispiel analysieren

Teil 2: Ping-Client (simpel)

Teil 3: Ping-Client (erweitert)

Hinweise:

public class PingServer
{
   private static final double LOSS_RATE = 0.3;
   private static final int AVERAGE_DELAY = 100;  // milliseconds

   public static void main(String[] args) throws Exception
   {
      // Get command line argument.
      if (args.length != 1) {
         System.out.println("Required arguments: port");
         return;
      }
      int port = Integer.parseInt(args[0]);

      // Create random number generator for use in simulating packet loss and network delay.
      Random random = new Random();

      // Create a datagram socket for receiving and sending UDP packets
      // through the port specified on the command line.
      DatagramSocket socket = new DatagramSocket(port);

      // Processing loop.
      while (true) {
         // Create a datagram packet to hold incomming UDP packet.
         DatagramPacket request = new DatagramPacket(new byte[1024], 1024);

         // Block until the host receives a UDP packet.
         socket.receive(request);
         
         // Print the recieved data.
         printData(request);

         // Decide whether to reply, or simulate packet loss.
         if (random.nextDouble() < LOSS_RATE) {
            System.out.println("   Reply not sent.");
            continue; 
         }

         // Simulate network delay.
         Thread.sleep((int) (random.nextDouble() * 2 * AVERAGE_DELAY));

         // Send reply.
         InetAddress clientHost = request.getAddress();
         int clientPort = request.getPort();
         DatagramPacket reply = new DatagramPacket(request.getData(), request.getLength(), clientHost, clientPort);
         socket.send(reply);

         System.out.println("   Reply sent.");
      }
   }

    // Print ping data to the standard output stream.
    private static void printData( DatagramPacket request) {
        String line = new String(request.getData(), 0, request.getLength(), StandardCharsets.UTF_8).trim();
        System.out.println("Received from " + request.getAddress().getHostAddress() + ": " + line);
    }
}

Lösungen:

  1. Probleme:
    • Client
      • Zeile 17 ist überflüssig, da das Array nicht verwendet wird (byte[] sendData )
      • Nur empfangene Daten in String umwandeln (Array hat fix 1024 Byte): String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
    • Server
      • Liefert das gesamte 1024-Byte Array zurück
      • Nebeneffekt: alte Daten bleiben im Array
      • Lösung: String sentence = new String(receivePacket.getData(), 0, receivePacket.getLength());
import java.io.*;
import java.net.*;

class UDPClient {
    public static void main(String[] args) throws Exception {
        // 1. Usereingabe & Socket initialisieren
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        DatagramSocket socket = new DatagramSocket();
        InetAddress ip = InetAddress.getByName("localhost");

        // 2. Text einlesen und senden
        System.out.print("Eingabe: ");
        byte[] sendData = reader.readLine().getBytes();
        
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ip, 9876);
        socket.send(sendPacket);

        // 3. Antwort empfangen (Buffer von 1024 Bytes reicht als Auffangbehälter)
        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
        socket.receive(receivePacket);

        // 4. Nur empfangene Bytes in String umwandeln & ausgeben (ansonsten String voller Null-Bytes (\0) )
        String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("FROM SERVER: " + response);

        socket.close();
    }
}


import java.net.*;

public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9876);
        byte[] buffer = new byte[1024];

        while (true) {
            // Empfangen
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet);

            // Verarbeiten (Großbuchstaben)
            String text = new String(packet.getData(), 0, packet.getLength()).toUpperCase();
            byte[] sendData = text.getBytes();

            // Zurücksenden an Absender
            DatagramPacket response = new DatagramPacket(
                sendData, sendData.length, packet.getAddress(), packet.getPort()
            );
            socket.send(response);
        }
    }
}
public class PingClient {

  public static void main(String[] args) throws Exception {
    if (args.length != 2) {
      System.out.println("Required arguments: host port");
      return;
    }
    InetAddress host = InetAddress.getByName(args[0]);
    int port = Integer.parseInt(args[1]);

    DatagramSocket socket = new DatagramSocket();
    socket.setSoTimeout(1000);

    int received = 0;
    long min = Long.MAX_VALUE, max = 0, sum = 0;

    for (int seq = 0; seq < 10; seq++) {
      byte[] data = ("PING " + seq + " " + System.currentTimeMillis() + " \r\n").getBytes();
      long start = System.currentTimeMillis();
      socket.send(new DatagramPacket(data, data.length, host, port));

      try {
        socket.receive(new DatagramPacket(new byte[1024], 1024));
        long rtt = System.currentTimeMillis() - start;
        System.out.println("seq=" + seq + " rtt=" + rtt + " ms");
        received++;
        sum += rtt;
        min = Math.min(min, rtt);
        max = Math.max(max, rtt);
      } catch (SocketTimeoutException e) {
        System.out.println("seq=" + seq + " timed out");
      }
    }

    if (received > 0) {
      System.out.println("RTT min/avg/max = " + min + "/" + (sum / received) + "/" + max + " ms");
    }
    socket.close();
  }
}

Fakultativ