Java - Server und java.util.TimerTask


#1

Hi,

ich versuche gerade eine Kommunikation zwischen zwei Programmen aufzubauen bzw. erstmal einen Server der bestimmte Aufgaben bearbeitet.
Als Grundlage habe ich mir ein Chatprogramm genommen. Ist das erste mal, dass ich überhaupt was damit mache (Fehlerbehandlung und Co mal ignorieren)

{
	ExecutorService executor = Executors.newFixedThreadPool(30);
	Long startTime = System.currentTimeMillis(); //Startzeit des Servers
	Timer timer = new Timer();
		
	ServerSocket server = null;
	try 
	{
		server = new ServerSocket(9000); //Server erstellen
		System.out.println("Server wurde gestartet! Port: " + server.getLocalPort());
	} 
	catch (IOException e1) {e1.printStackTrace();} 
		
	timer.schedule(new ServerTaskRuntime(startTime), 0, 1000*60*60); //Speichert Server-Laufzeit in .txt 
	 timer.schedule(new ServerTaskClients(server,  executor),0); //
}```


Zur Aufgabenverwaltung hab ich die Timer-Klasse von Java benutzt. Er soll später in regelmäßigen Abständen den Serverstatus speichern, Daten aus dem Internet abfragen und einen Screenshot machen.


Ein Problem gibt es jetzt bei der Klasse **ServerTaskClients**. Da ist eine Endlosschleife drinne und er wartet auf eine ankommende Verbindung von Clients.

```	public ServerTaskClients(ServerSocket server, ExecutorService executorService)
	{
		this.server = server;
		this.executorService = executorService;
	}
	
@Override
public void run() 
{
	 Socket client = null;
		
	 while(true)
	 {
		 try 
		 {
 			client = server.accept();
 		} 
		 catch (IOException e) {e.printStackTrace();}
		
		Thread clientThread = new Thread((new Handler(client)));
		 clientThread.start();
		 executorService.execute(new Handler(client));
	}
}```

Jetzt gibt es zwei Sachen. Einmal läuft in der Aufgabe eine Endlosschleife, weshalb die Aufgabe nie beendet wird und der Server natürlich andere Aufgaben nicht ausführen kann. Weiterhin soll die CPU so wenig wie möglich beansprucht werden (soll später auf Raspberry Pi laufen). Ich weiß nicht genau was er bei **client = server.accept();** macht. Im Grunde müssten die einzelnen Aufgaben als Thread laufen. Meine Idee war jetzt, dass mit **timer.schedule(new ServerTask....)** ein Thread gestartet und dieser dann in diesen Thread-Pool geschmissen wird. Dadurch haben die Tasks nur die Aufgabe die Threads zu starten und sind nicht mehr selber für die Ausführung verantwortlich. Das schaut für mich aber auch nicht wirklich nach der Lösung aus.

mfg

#2

Warten bis irgend ein Depp übers Netzwerk kommt und Deinen Server ärgern will. Das geht dort direkt in einen Deadlock, der kommt erst raus wenn sich jemand verbindet.

Mach mal einen Thread für den Server.

while(!ende) {
    new Client(server.accept());
}

und dann für jeden einzelnen Client ein Thread.

public class Client implements Runnable {
    public Client(Socket socket) {
        // Client-Socket merken
        // diverse andere Dinge
        // Task starten
    }
}

und im run() kannst Du dann wieder alles auslesen.


#3

Die Clients sind schon alle eigene Threads.

Kann auch sein, dass ich dich jetzt falsch verstanden habe oder ich es nicht genau erklärt habe. Der Server soll nach meiner Vorstellung bestimmte Tasks ausführen. Prinzipiell braucht der Server erst mal keine Clients. Die geben dann nur über das Netzwerk Befehle oder besorgen sich Daten.

Problem sind die Tasks. Die brauchen natürlich für die Ausführung unterschiedlich lange. Im Falle von ServerTaskClients ist halt die Endlosschleife das Problem. Betrifft nicht nur die Endlosschleifen, sondern auch Tasks die für die Ausführung länger brauchen und den Start von anderen Taks verzögern. Von daher sollten und müssen die Tasks vermutlich als Thread laufen und der Server ist nicht mehr für die Abarbeitung zuständig, sondern startet nur noch die Task-Threads. Wäre jetzt mein Ansatz.
Jetzt ist aber die Frage wie das in Verbindung mit java.util.timerTask geht.


#4

Handler auf Sockets warten üblicherweise endlos an ihren Sockets,

für Clients noch denkbar nur ab und zu vorbeizuschauen und zu hoffen dass eine avaiable()-Methode korrekt aussagt ob Daten vorhanden oder nicht,
dann könnte man das als Task erledigen, von einem Timer 1x pro Sekunde,
wenn auch unschöne Verzögerung, keine direkte Reaktion auf Ereignisse

für die accept()-Methode des Servers habe ich zu einem eigenen Thread noch keine Alternative gesehen, da fällt der Timer komplett aus

Threads sind gar nicht so teuer, jedenfalls für ein Server-Programm was eh die meiste Zeit nichts tut,
es geht hier ja nicht um Endlosberechnungen wie Bitcoin-Mining über Jahre, wo jede CPU-Millisekunde zählt?

mit einem Standardeinsatz von Threads wie in allen Beispielen dürftest du besser fahren,
sonst wie gesagt evtl. die ClientHandler als Tasks, und die dann so designen wie bei Tasks üblich:
kurze Abarbeitungen und fertig,
ergo keine Schleife, nur einmal nachschauen ob Nachricht da, evtl. verarbeiten und fertig


        Thread clientThread = new Thread((new Handler(client)));
         clientThread.start();
         executorService.execute(new Handler(client));

geht in jedem Fall nicht, das ist ja Thread + Timer, außerdem zwei Handler-Objekte, die vielleicht einzeln Streams öffnen usw. schlimmes?
ist das nur Aufzählung beider Möglichkeiten oder steht das so bewußt da, zu welchen vermeintlichen Zweck?