modify justfile (to start server with just server); improved Readme; modify server (log-output is now better structured); modify client
parent
824c41012e
commit
8aae306704
52
README.md
52
README.md
|
|
@ -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`.*
|
||||||
4
justfile
4
justfile
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue