CS_LEVEL_22_SOLUTION - OnlyCook/abitur-elite-code GitHub Wiki
Level 22 – Musterlösung: Multi-User Hub (Threads)
Lösung
public class SmartHomeServer
{
private int port;
private SmartHomeHub hub;
private ServerSocket serverSocket;
public SmartHomeServer(int port, SmartHomeHub hub)
{
this.port = port;
this.hub = hub;
this.serverSocket = new ServerSocket(port);
}
public void RunServer()
{
while (true)
{
Socket socket = serverSocket.Accept();
ServerThread st = new ServerThread(socket, hub);
st.Start();
}
}
}
public class ServerThread : Thread
{
private Socket clientSocket;
private SmartHomeHub hub;
public ServerThread(Socket cs, SmartHomeHub hub)
{
this.clientSocket = cs;
this.hub = hub;
}
public override void Run()
{
string befehl = "";
while (befehl != "QUIT")
{
befehl = clientSocket.ReadLine();
switch (befehl)
{
case "PING":
clientSocket.Write("+PONG\n");
break;
case "INFO":
clientSocket.Write("+IP " + clientSocket.GetRemoteHostIP() + "\n");
break;
case "LOGIN":
Random rnd = new Random();
int token = rnd.NextInt(10000);
clientSocket.Write("+TOKEN " + token + "\n");
break;
default:
break;
}
}
clientSocket.Close();
}
}
public class SmartHomeHub
{
public SmartHomeHub() {}
}
Erklärung
Das neue Konzept: Threads
Bisher hat der Server immer nur einen Client bedient – und während er auf dessen Befehle wartete, konnte kein weiterer Client eine Verbindung aufbauen. Threads lösen dieses Problem: Ein Thread ist ein eigenständiger Ausführungsstrang im Programm. Startet man für jeden Client einen eigenen Thread, können mehrere Clients gleichzeitig bedient werden.
Im Abitur erbt eine eigene Thread-Klasse immer von Thread und überschreibt die Methode Run() – genau das passiert hier mit ServerThread.
SmartHomeServer – die Endlosschleife
public void RunServer()
{
while (true)
{
Socket socket = serverSocket.Accept();
ServerThread st = new ServerThread(socket, hub);
st.Start();
}
}
while (true) ist eine bewusste Endlosschleife – der Server soll dauerhaft laufen und nie von alleine aufhören, auf neue Clients zu warten. Für jeden Client, der sich verbindet, wird sofort ein neuer ServerThread erstellt und mit Start() gestartet. Start() ist eine Methode der Basisklasse Thread und sorgt dafür, dass Run() in einem eigenen Ausführungsstrang läuft – RunServer() kehrt danach sofort zurück und wartet schon auf den nächsten Client.
ServerThread – Vererbung von Thread
public class ServerThread : Thread
ServerThread erbt von Thread. Das Klassendiagramm zeigt das mit einem Vererbungspfeil. Durch die Vererbung steht Start() automatisch zur Verfügung – die einzige Pflicht ist, Run() zu überschreiben.
public override void Run()
override signalisiert, dass diese Methode die gleichnamige Methode der Basisklasse ersetzt. Ohne override würde beim Aufruf von Start() die leere Run()-Methode der Basisklasse ausgeführt – also nichts passieren.
switch statt verschachteltem if/else if
switch (befehl)
{
case "PING":
clientSocket.Write("+PONG\n");
break;
case "INFO":
clientSocket.Write("+IP " + clientSocket.GetRemoteHostIP() + "\n");
break;
case "LOGIN":
Random rnd = new Random();
int token = rnd.NextInt(10000);
clientSocket.Write("+TOKEN " + token + "\n");
break;
default:
break;
}
Wenn es viele mögliche Werte für eine Variable gibt, ist switch übersichtlicher als eine lange if/else if-Kette. Für jeden möglichen Wert gibt es einen case-Block, der mit break abgeschlossen wird. Der default-Block greift, wenn kein case passt – hier passiert dann nichts, aber default sollte trotzdem stehen, um den Fall explizit zu behandeln (und QUIT löst korrekt keinen Fehler aus, sondern verlässt einfach nach der nächsten Schleifenprüfung die while-Schleife).
Random – Zufallszahlen generieren
Random rnd = new Random();
int token = rnd.NextInt(10000);
new Random() erstellt ein Zufallszahlengenerator-Objekt. NextInt(10000) liefert eine zufällige ganze Zahl von 0 (einschließlich) bis 10000 (ausschließlich) – also 0 bis 9999. Das entspricht dem Java-Pendant nextInt(n) aus dem Abitur; in dieser App heißt die übersetzte Methode NextInt(n).
SmartHomeHub – eine leere Klasse
public class SmartHomeHub
{
public SmartHomeHub() {}
}
SmartHomeHub hat in diesem Level noch keinen Inhalt – sie existiert, weil das Klassendiagramm sie vorschreibt und sie als gemeinsamer Zustand für zukünftige Level dient. Im Abitur kommt das eher nicht vor, jedoch ist es hier stets erwartet, um zu sehen wie hier leere Hilfsklassen im Programm verwendet werden (die App tut dies aber, damit das Level komplizierter ist).