modify justfile (to start server with just server); improved Readme; modify server (log-output is now better structured); modify client

Dimitrios
Dimitrios 2026-04-13 14:42:07 +02:00
parent 824c41012e
commit 8aae306704
4 changed files with 85 additions and 90 deletions

View File

@ -1,30 +1,44 @@
# Verteilte_Systeme
# PU 1: "Praktikum Service-Discovery"
Bearbeitung Prüfungsvorleistung für Verteilte Systeme
Dieses Projekt wurde von der **Gruppe D** entwickelt:
**Antoniadis Dimitrios, Dexheimer Fynn, Corina Schmitt**
# Syslog-Logging-System (RFC 5424)
Dieses Projekt implementiert einen Syslog-Server auf Basis von UDP gemäß dem RFC 5424 Standard. Es beinhaltet eine automatische Service-Discovery via Broadcast.
Dieses Projekt implementiert einen Syslog-Server und einen Test-Client auf Basis von UDP. Es beinhaltet eine automatische Service-Discovery.
> **Hinweis:** Um den Server zu testen, wird ein entsprechender Client benötigt. Das System setzt die installierten Technologien **Java**, **Maven** voraus. **Just** ist Optional.
## Projektstruktur & Klassen
* **`pom.xml`**: Die Maven-Konfigurationsdatei. Sie verwaltet Abhängigkeiten und die Java-Version.
* **`justfile`**: Automatisierungsskript für Windows (PowerShell) und Linux, um Befehle wie `just clean` oder `just exec` zu nutzen.
* **`pom.xml`**: Maven-Konfigurationsdatei für Abhängigkeiten und Build-Prozess.
* **`justfile`**: Automatisierungsskript für einfache Ausführung (`just server`).
* **`vs.SyslogServer`**: Der Hauptdienst. Er startet zwei parallele Threads:
* **Syslog-Service**: Empfängt Log-Nachrichten auf Port 514.
* **Discovery-Service**: Antwortet auf Broadcast-Anfragen auf Port 8888.
* **`vs.SyslogClient`**: Test-Client, der den Server via Broadcast sucht und eine standardkonforme Nachricht sendet.
* **`vs.SyslogMessage`**: Repräsentiert eine Nachricht gemäß RFC 5424.
* **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Validierung und Formatierung von Zeichenketten gemäß Spezifikation.
* **Syslog-Service**: Empfängt Log-Nachrichten auf Port 514, validiert die Länge und trennt Metadaten optisch vom Nachrichteninhalt.
* **Discovery-Service**: Antwortet auf Broadcast-Anfragen auf Port 8888, um Clients die Server-IP mitzuteilen.
* **`vs.SyslogMessage`**: Kernklasse zur Erzeugung/Repräsentation standardkonformer Nachrichten (inkl. Header, Zeitstempel und Structured Data).
* **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Einhaltung der RFC-Spezifikationen.
## Funktionsablauf (Service Discovery)
1. **Broadcast**: Der Client sendet ein UDP-Paket an `255.255.255.255:8888`.
2. **Antwort**: Der Server empfängt den Broadcast und sendet ein leeres UDP-Paket an den Client zurück.
3. **Identifikation**: Der Client liest die IP-Adresse des Absenders aus dem Antwort-Paket aus.
4. **Logging**: Der Client sendet die eigentliche Syslog-Nachricht direkt an die ermittelte IP auf Port 514.
## Funktionsablauf (Service Discovery & Logging)
1. **Broadcast**: Ein Client sendet ein UDP-Paket an die Broadcast-Adresse `255.255.255.255:8888`.
2. **Antwort**: Der Server empfängt den Suchruf und sendet ein Bestätigungs-Paket an den Client zurück.
3. **Identifikation**: Der Client extrahiert die IP-Adresse des Servers aus dem Antwort-Paket.
4. **Logging**: Der Client formatiert die Nachricht nach RFC 5424 und sendet sie direkt an den Server (Port 514).
5. **Verarbeitung**: Der Server empfängt das Datenpaket, validiert die Länge, entfernt das Byte Order Mark (BOM) und trennt die RFC-Metadaten optisch vom eigentlichen Nachrichteninhalt für eine strukturierte Konsolenausgabe.
## Ausführung
* Server starten: `just exec SyslogServer`
* Client starten: `just exec SyslogClient`
* Aufräumen: `just clean`
Stellen Sie sicher, dass **Java**, **Maven** und **Just** auf Ihrem System installiert sind.
### 1. Konfiguration für Linux/macOS
Da das `justfile` standardmäßig für die PowerShell optimiert ist, passen Sie bei der Nutzung von Unix-basierten Systemen bitte die erste Zeile im `justfile` an:
* Ändern Sie `set shell := ["powershell.exe", "-c"]` zu `set shell := ["sh", "-c"]`.
### 2. Wichtige Befehle
Nutzen Sie `just`, um die folgenden Aufgaben automatisiert auszuführen:
* **Kompilieren**: `just compile`
* **Server starten**: `just server`
* **Projekt aufräumen**: `just clean`
*Hinweis: Weitere Befehle für Tests oder die Javadoc-Generierung finden Sie direkt im `justfile`.*

View File

@ -4,6 +4,10 @@ set shell := ["powershell.exe", "-c"]
default:
mvn clean compile test
server: compile
mvn exec:java "-Dexec.mainClass=vs.SyslogServer"
client +args="": compile
mvn exec:java "-Dexec.mainClass=vs.SyslogClient" "-Dexec.args={{args}}"
exec class +args="": compile
mvn exec:java "-Dexec.mainClass=vs.{{class}}" "-Dexec.args={{args}}"
clean:

View File

@ -7,28 +7,32 @@ import java.nio.charset.StandardCharsets;
public class SyslogClient {
private static final int PORT = 514;
private static final int SYSLOG_PORT = 514;
private static final int DISCOVERY_PORT = 8888;
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(true);
// Discovery
// 1. SERVICE DISCOVERY
System.out.println("[CLIENT] Suche Syslog-Server via Broadcast...");
byte[] discoverMsg = "DISCOVER".getBytes();
DatagramPacket packet = new DatagramPacket(
DatagramPacket discoverPacket = new DatagramPacket(
discoverMsg, discoverMsg.length,
InetAddress.getByName("255.255.255.255"), 8888
InetAddress.getByName("255.255.255.255"), DISCOVERY_PORT
);
socket.send(packet);
socket.send(discoverPacket);
// Antwort empfangen
// Auf Antwort warten
byte[] buffer = new byte[256];
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
System.out.println("Server gefunden: " + response.getAddress());
// ECHTE RFC 5424 Syslog-Nachricht aus AsciiChars & SyslogMessage
// args als massage, ansonsten default message
String text = (args.length > 0) ? String.join(" ", args): "Dies ist eine standardkonforme Testnachricht";
InetAddress serverAddress = response.getAddress();
System.out.println("[CLIENT] Server gefunden unter: " + serverAddress.getHostAddress());
// 2. NACHRICHT ERSTELLEN
String text = (args.length > 0) ? String.join(" ", args) : "Standard-Testnachricht";
SyslogMessage msg = new SyslogMessage(
SyslogMessage.Facility.USER,
@ -41,19 +45,16 @@ public class SyslogClient {
new SyslogMessage.TextMessage(text)
);
// Die Klasse wandelt alles in den perfekten String um (inklusive Zeitstempel)
// 3. SENDEN
String logString = msg.toString();
System.out.println("Sende generierten String: " + logString);
// Nachricht als UTF-8 Bytes senden
byte[] data = logString.getBytes(StandardCharsets.UTF_8);
DatagramPacket syslogPacket = new DatagramPacket(
data, data.length, response.getAddress(), PORT
data, data.length, serverAddress, SYSLOG_PORT
);
socket.send(syslogPacket);
System.out.println("Log gesendet!");
System.out.println("[CLIENT] Nachricht gesendet: " + logString);
socket.close();
}

View File

@ -3,106 +3,82 @@ package vs;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
public class SyslogServer {
private static final int SYSLOG_PORT = 514;
private static final int DISCOVERY_PORT = 8888;
private static final int MAX_MESSAGE_LENGTH = 1024; // konservativ (UDP safe)
private static final int MAX_MESSAGE_LENGTH = 1024;
public static void main(String[] args) {
System.out.println("Starting Syslog Server...");
System.out.println("=== Syslog Server Management ===");
// Thread für Syslog
// Startet den Syslog-Empfänger
new Thread(() -> runSyslogServer()).start();
// Thread für Discovery
// Startet den Discovery-Dienst
new Thread(() -> runDiscoveryServer()).start();
}
private static void runSyslogServer() {
try (DatagramSocket socket = new DatagramSocket(SYSLOG_PORT)) {
// Puffergröße 2048 gemäß RFC 5424 Empfehlung.
// Ermöglicht den Empfang kompletter Netzwerkpakete (MTU), um zu lange
// Nachrichten aktiv zu erkennen und abzulehnen, statt sie nur abzuschneiden.
byte[] buffer = new byte[2048];
System.out.println("Syslog Server läuft auf Port " + SYSLOG_PORT);
System.out.println("[INFO] Syslog-Dienst aktiv auf Port " + SYSLOG_PORT);
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
int length = packet.getLength();
// Länge prüfen
if (length > MAX_MESSAGE_LENGTH) {
System.out.println("Nachricht zu lang von " + packet.getAddress());
System.err.println("[WARN] Nachricht von " + packet.getAddress() + " zu lang. Ignoriert.");
continue;
}
String message = new String(packet.getData(), 0, length, StandardCharsets.UTF_8);
String raw = new String(packet.getData(), 0, length, StandardCharsets.UTF_8);
String clean = raw.replace("\uFEFF", "").replace("", "");
// BOM-zeichen ersetzten
message = message.replace("", "Nachricht: ");
// Trennung von Metadaten und Inhalt
int splitIndex = clean.lastIndexOf("]") + 1;
String metadata = "Unbekannt";
String messageContent = clean;
// Zur Sicherheit AUCH noch das Unicode-Zeichen des BOM-Zeichens
message = message.replace("\uFEFF", "Nachricht: ");
if (splitIndex > 0 && splitIndex < clean.length()) {
metadata = clean.substring(0, splitIndex).trim();
messageContent = clean.substring(splitIndex).trim();
}
// Ausgabe mit Client-IP
System.out.println(
"[SYSLOG] Von " +
packet.getAddress().getHostAddress() +
":" +
packet.getPort() +
" -> " +
message
);
String separator = "==================================================";
System.out.println("\n" + separator);
System.out.println("NEUE SYSLOG-NACHRICHT");
System.out.println("Quelle: " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
System.out.println("Metadaten: " + metadata);
System.out.println("Inhalt: " + messageContent);
System.out.println(separator);
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("[ERROR] Syslog-Server Fehler: " + e.getMessage());
}
}
private static void runDiscoveryServer() {
try (DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT)) {
byte[] buffer = new byte[256];
System.out.println("Discovery Service läuft auf Port " + DISCOVERY_PORT);
System.out.println("[INFO] Discovery-Service aktiv auf Port " + DISCOVERY_PORT);
while (true) {
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
socket.receive(request);
InetAddress clientAddress = request.getAddress();
int clientPort = request.getPort();
System.out.println(
"[DISCOVERY] Anfrage von " +
clientAddress.getHostAddress()
);
// Antwort leer(laut Aufgabe)
byte[] responseData = new byte[0];
DatagramPacket response = new DatagramPacket(
responseData,
responseData.length,
clientAddress,
clientPort
new byte[0], 0, request.getAddress(), request.getPort()
);
socket.send(response);
System.out.println(
"[DISCOVERY] Antwort gesendet an " +
clientAddress.getHostAddress()
);
System.out.println("[DISCOVERY] Server-IP an Client mit der IP [" + request.getAddress().getHostAddress() + "] gemeldet.");
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("[ERROR] Discovery-Fehler: " + e.getMessage());
}
}
}