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 ## Projektstruktur & Klassen
* **`pom.xml`**: Die Maven-Konfigurationsdatei. Sie verwaltet Abhängigkeiten und die Java-Version. * **`pom.xml`**: Maven-Konfigurationsdatei für Abhängigkeiten und Build-Prozess.
* **`justfile`**: Automatisierungsskript für Windows (PowerShell) und Linux, um Befehle wie `just clean` oder `just exec` zu nutzen. * **`justfile`**: Automatisierungsskript für einfache Ausführung (`just server`).
* **`vs.SyslogServer`**: Der Hauptdienst. Er startet zwei parallele Threads: * **`vs.SyslogServer`**: Der Hauptdienst. Er startet zwei parallele Threads:
* **Syslog-Service**: Empfängt Log-Nachrichten auf Port 514. * **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. * **Discovery-Service**: Antwortet auf Broadcast-Anfragen auf Port 8888, um Clients die Server-IP mitzuteilen.
* **`vs.SyslogClient`**: Test-Client, der den Server via Broadcast sucht und eine standardkonforme Nachricht sendet. * **`vs.SyslogMessage`**: Kernklasse zur Erzeugung/Repräsentation standardkonformer Nachrichten (inkl. Header, Zeitstempel und Structured Data).
* **`vs.SyslogMessage`**: Repräsentiert eine Nachricht gemäß RFC 5424. * **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Einhaltung der RFC-Spezifikationen.
* **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Validierung und Formatierung von Zeichenketten gemäß Spezifikation.
## Funktionsablauf (Service Discovery)
1. **Broadcast**: Der Client sendet ein UDP-Paket an `255.255.255.255:8888`. ## Funktionsablauf (Service Discovery & Logging)
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. 1. **Broadcast**: Ein Client sendet ein UDP-Paket an die Broadcast-Adresse `255.255.255.255:8888`.
4. **Logging**: Der Client sendet die eigentliche Syslog-Nachricht direkt an die ermittelte IP auf Port 514. 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 ## Ausführung
* Server starten: `just exec SyslogServer`
* Client starten: `just exec SyslogClient` Stellen Sie sicher, dass **Java**, **Maven** und **Just** auf Ihrem System installiert sind.
* Aufräumen: `just clean`
### 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: default:
mvn clean compile test 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 exec class +args="": compile
mvn exec:java "-Dexec.mainClass=vs.{{class}}" "-Dexec.args={{args}}" mvn exec:java "-Dexec.mainClass=vs.{{class}}" "-Dexec.args={{args}}"
clean: clean:

View File

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

View File

@ -3,106 +3,82 @@ package vs;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
public class SyslogServer { public class SyslogServer {
private static final int SYSLOG_PORT = 514; private static final int SYSLOG_PORT = 514;
private static final int DISCOVERY_PORT = 8888; 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) { 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(); new Thread(() -> runSyslogServer()).start();
// Thread für Discovery // Startet den Discovery-Dienst
new Thread(() -> runDiscoveryServer()).start(); new Thread(() -> runDiscoveryServer()).start();
} }
private static void runSyslogServer() { private static void runSyslogServer() {
try (DatagramSocket socket = new DatagramSocket(SYSLOG_PORT)) { 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]; byte[] buffer = new byte[2048];
System.out.println("[INFO] Syslog-Dienst aktiv auf Port " + SYSLOG_PORT);
System.out.println("Syslog Server läuft auf Port " + SYSLOG_PORT);
while (true) { while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); socket.receive(packet);
int length = packet.getLength(); int length = packet.getLength();
// Länge prüfen
if (length > MAX_MESSAGE_LENGTH) { 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; 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: ");
// Zur Sicherheit AUCH noch das Unicode-Zeichen des BOM-Zeichens // Trennung von Metadaten und Inhalt
message = message.replace("\uFEFF", "Nachricht: "); int splitIndex = clean.lastIndexOf("]") + 1;
String metadata = "Unbekannt";
String messageContent = clean;
// Ausgabe mit Client-IP if (splitIndex > 0 && splitIndex < clean.length()) {
System.out.println( metadata = clean.substring(0, splitIndex).trim();
"[SYSLOG] Von " + messageContent = clean.substring(splitIndex).trim();
packet.getAddress().getHostAddress() + }
":" +
packet.getPort() + String separator = "==================================================";
" -> " + System.out.println("\n" + separator);
message 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) { } catch (IOException e) {
e.printStackTrace(); System.err.println("[ERROR] Syslog-Server Fehler: " + e.getMessage());
} }
} }
private static void runDiscoveryServer() { private static void runDiscoveryServer() {
try (DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT)) { try (DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT)) {
byte[] buffer = new byte[256]; byte[] buffer = new byte[256];
System.out.println("[INFO] Discovery-Service aktiv auf Port " + DISCOVERY_PORT);
System.out.println("Discovery Service läuft auf Port " + DISCOVERY_PORT);
while (true) { while (true) {
DatagramPacket request = new DatagramPacket(buffer, buffer.length); DatagramPacket request = new DatagramPacket(buffer, buffer.length);
socket.receive(request); 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( DatagramPacket response = new DatagramPacket(
responseData, new byte[0], 0, request.getAddress(), request.getPort()
responseData.length,
clientAddress,
clientPort
); );
socket.send(response); socket.send(response);
System.out.println("[DISCOVERY] Server-IP an Client mit der IP [" + request.getAddress().getHostAddress() + "] gemeldet.");
System.out.println(
"[DISCOVERY] Antwort gesendet an " +
clientAddress.getHostAddress()
);
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); System.err.println("[ERROR] Discovery-Fehler: " + e.getMessage());
} }
} }
} }