CS_LEVEL_23_SOLUTION - OnlyCook/abitur-elite-code GitHub Wiki
public class SicherheitsServer
{
private int port;
private SicherheitsZentrale zentrale;
private ServerSocket serverSocket;
public SicherheitsServer(int port, SicherheitsZentrale z)
{
this.port = port;
this.zentrale = z;
this.serverSocket = new ServerSocket(port);
}
public void RunServer()
{
while (true)
{
Socket socket = serverSocket.Accept();
new SicherheitsThread(socket, zentrale).Start();
}
}
}
public class SicherheitsThread : Thread
{
private Socket clientSocket;
private SicherheitsZentrale zentrale;
public SicherheitsThread(Socket cs, SicherheitsZentrale z)
{
this.clientSocket = cs;
this.zentrale = z;
}
public override void Run()
{
string befehl = clientSocket.ReadLine();
if (befehl.StartsWith("LOGIN;"))
{
string[] parts = befehl.Split(';');
bool ok = VergleicheZugangsdaten(parts[1], parts[2]);
if (ok)
{
clientSocket.Write("+OK Willkommen\n");
while (befehl != "QUIT")
{
befehl = clientSocket.ReadLine();
switch (befehl)
{
case "STATUS":
Alarmanlage alarmanlage = zentrale.GetAlarmanlage();
bool aktiv = alarmanlage.GetAktiv();
string msg = aktiv ? "ON" : "OFF";
clientSocket.Write("+OK ALARM_" + msg + "\n");
break;
case "TOGGLE":
alarmanlage = zentrale.GetAlarmanlage();
alarmanlage.SetAktiv(!alarmanlage.GetAktiv());
string log = "Alarmanlage umgeschaltet";
zentrale.GetLog().GetEintraege().Add(log);
clientSocket.Write("+OK Umschaltung erfolgreich\n");
break;
case "QUIT":
clientSocket.Write("+OK Bye\n");
break;
}
}
}
else
{
clientSocket.Write("-ERR Login fehlgeschlagen\n");
}
}
clientSocket.Close();
}
private bool VergleicheZugangsdaten(string user, string pin)
{
BenutzerVerwaltung verwaltung = zentrale.GetVerwaltung();
if (verwaltung.GetAdminUser() == user)
{
if (verwaltung.GetAdminPin() == pin)
{
return true;
}
}
return false;
}
}Dieses Level kombiniert alles aus der Sektion: Threads, Socket-Kommunikation, Protokollverarbeitung und Objektzugriff über mehrere Ebenen. Es gibt keine wirklich neuen Konzepte – dafür ist die Eigenständigkeit gefragt, alles sauber zusammenzusetzen.
new SicherheitsThread(socket, zentrale).Start();Statt das Objekt erst in einer Variable zu speichern und dann Start() aufzurufen, wird beides direkt in einer Zeile erledigt. Das funktioniert, weil new SicherheitsThread(...) das neue Objekt zurückgibt und man sofort eine Methode darauf aufrufen kann. Diese Variante ist kompakt und im Abitur durchaus verbreitet.
string befehl = clientSocket.ReadLine();
if (befehl.StartsWith("LOGIN;"))
{
string[] parts = befehl.Split(';');
bool ok = VergleicheZugangsdaten(parts[1], parts[2]);Der erste eingehende Befehl wird direkt vor der Schleife gelesen – das Sequenzdiagramm zeigt, dass der Client als allererstes eine Login-Anfrage schickt. Split(';') zerlegt "LOGIN;admin;admin123" in drei Teile: parts[0] = "LOGIN", parts[1] = "admin", parts[2] = "admin123". Benutzername und PIN werden direkt an VergleicheZugangsdaten übergeben.
bool aktiv = alarmanlage.GetAktiv();
string msg = aktiv ? "ON" : "OFF";
clientSocket.Write("+OK ALARM_" + msg + "\n");aktiv ? "ON" : "OFF" ist der ternäre Operator – eine Kurzform für if/else, die direkt einen Wert zurückgibt. Die Syntax lautet: Bedingung ? WertWennWahr : WertWennFalsch. Hier: ist aktiv wahr, wird "ON" zugewiesen, sonst "OFF".
Der fertige String wird dann mit + zusammengesetzt: "+OK ALARM_" + "ON" ergibt "+OK ALARM_ON". Das ist völlig gleichwertig zu einem String-Interpolationsausdruck wie $"+OK ALARM_{msg}\n" – beide Schreibweisen sind korrekt.
Falls man sich jedoch mit so einem Beispiel unsicher ist, kann man auch einfach folgendes tun:
bool aktiv = alarmanlage.GetAktiv();
if (aktiv)
{
clientSocket.Write("+OK ALARM_ON\n");
}
else
{
clientSocket.Write("+OK ALARM_OFF\n");
}zentrale.GetLog().GetEintraege().Add(log);Hier wird keine neue Variable für jedes Zwischenergebnis angelegt – stattdessen werden die Methodenaufrufe direkt verkettet. Das funktioniert, weil jede Methode ein Objekt zurückgibt, auf dem man sofort die nächste Methode aufrufen kann:
-
zentrale.GetLog()→ liefert dasProtokollLog-Objekt der Zentrale -
.GetEintraege()→ liefert dieList<string>der Einträge in diesem Log -
.Add(log)→ fügt den neuen Eintrag zur Liste hinzu
Man kann sich das wie eine Kette vorstellen: jeder Pfeil im Sequenzdiagramm, der tiefer ins Objektgeflecht führt, entspricht einem weiteren .-Aufruf im Code. Das Ergebnis ist kürzer als drei separate Variablendeklarationen – aber nur dann sinnvoll, wenn man das Zwischenergebnis (also GetLog() oder GetEintraege()) danach nicht nochmal braucht.
Wer es lieber wie gewohnt machen will oder nicht versteht, kann einfach folgendes nutzen oder einsehen:
ProtokollLog pLog = zentrale.GetLog();
List<string> eintraege = pLog.GetEintraege();
eintraege.Add(log);Wie in Mathe kann man sich hier vorstellen:
pLog = zentrale.GetLog() → pLog ist dasselbe wie zentrale.GetLog(),
eintraege = pLog.GetEintraege() → eintraege ist dasselbe wie zentrale.GetLog().GetEintraege(),
also ist danach eintraege.Add(log) exakt gleichwertig wie zentrale.GetLog().GetEintraege().Add(log).
private bool VergleicheZugangsdaten(string user, string pin)
{
BenutzerVerwaltung verwaltung = zentrale.GetVerwaltung();
if (verwaltung.GetAdminUser() == user)
{
if (verwaltung.GetAdminPin() == pin)
{
return true;
}
}
return false;
}Die Methode greift über zentrale.GetVerwaltung() auf die BenutzerVerwaltung zu und vergleicht dort Benutzername und PIN getrennt. Das abschließende return false am Ende greift immer dann, wenn einer der beiden Checks fehlschlägt – es muss also nicht explizit in einem else-Zweig stehen. Diese Schreibweise – erst alle Erfolgsbedingungen prüfen, am Ende pauschal false zurückgeben – ist sauber und lesbar.
Wer einen One-Liner haben will:
private bool VergleicheZugangsdaten(string user, string pin) => zentrale.GetVerwaltung().GetAdminUser() == user && zentrale.GetVerwaltung().GetAdminPin() == pin ? true : false;