[JAVA] Best Practice für Sockets bei Netzwerkproblemen?

Larius

OutOfOrder
Teammitglied
Registriert
12 Juli 2013
Beiträge
3.831
Grüß euch,

einmal als Ausgangsbasis:

ich habe einen Multiuser-Chat-Server. Dieser Server hat pro User 2 Threads (Lesen/Schreiben), sprich bei einem Verbindungsabbruch (bsp. durch Hard-Reset wg. Terminierung eines Clients durch die IDE) wirft meine Incoming-Verbindung eine Exception und ich entferne daraufhin die Threads sowie die Verbindung aus meinem User-Pool.

Was ist jetzt die Best Practice für eine Socket Verbindung wenn es zu einem Problem kommt ala Netzwerk ist kurzfristig nicht verfügbar. Die Socket-Verbindung kann ich ja eigentlich weiterhin verwenden sobald das Netzwerk wieder funktioniert, oder? Soll ich dann einfach hergehen und eine gewisse Zeitspanne warten, bevor ich dann noch einmal von dem Socket probiere zu lesen und erst nach bsp. 3-5 Versuchen die Verbindung abbrechen? Oder ist ein direkter Kill der Verbindung deutlich besser?

LG
 
Was würde denn dagegen sprechen die Verbindung zu killen und eine neue aufzubauen, sobald der Client sich wieder meldet?
 
[...]ala Netzwerk ist kurzfristig nicht verfügbar[...]

Kann man so etwas nicht über den Timeout regeln? Grundsätzlich würde ich allerdings die Verbindung relativ fix killen und dann warten dass der Client sich neu verbindet. Solange dies nicht besonders aufwändig oder andersweitig problematisch ist würde ich keine halbtoten bis ganz toten Verbindungen versuchen aufrecht zu erhalten außer es gibt einen wirklich plausiblen Grund dafür.

MfG
Mr. J
 
  • Thread Starter Thread Starter
  • #4
Bzgl. Timeout: Da bin ich mir nicht ganz sicher wie bzw. ob ich einen Timeout am Socket einstellen kann. Notfalls hätte ich das nämlich wirklich über einen "Pfusch" via Try/Catch gemacht und bei genügend verstrichener Zeit die Verbindung gekillt, das wäre wohl auf das Gleiche wie ein Timeout gekommen.

Einen plausiblen Grund gibt es dafür nicht, ich kenne es halt aus Spielen wo nicht sofort, wenn die Verbindung weg ist, das Spiel unterbrochen wird sondern erst nach einer gewissen Zeit etwas geschieht (als Beispiel sei hier World of Warcraft genannt). Deshalb habe ich mich gefragt ob man die Socket-Verbindung einfach ohne Weiteres weiterverwenden kann oder man es sofort killen sollte. Wenn ihr aber der Meinung seid das man die Verbindung einfach killen und sobald alles wieder rennt einfach neu aufbauen soll werde ich das drinnen belassen.
 
Der Timeout sollte mit Socketname.setSoTimeout einstellbar sein, wenn die Zeit rum ist ohne das etwas passiert ist sollte eine Exception geworfen werden die man dann entsprechend fangen und den Socket schließen kann. Zur Frage ob man das sofort machen soll oder nicht würde ich halt danach gehen ob es besondere Vorteile oder Nachteile gibt. Ich weis nicht wie aufwändig es bzgl. Rechenzeit ist einen neuen Socket zu öffnen anstatt den Alten lieber etwas weiter offen zu lassen. Wenn die Zahl der verfügbaren Verbindungen zum Server allerdings ausreichend groß ist kann man sich durchaus erlauben etwas zu warten. Das könnte letzten Endes auf der Clientseite halt etwas komfortabler sein, da der Client dann nicht neu connecten muss (stelle ich mir zumindest grad so vor).

MfG
Mr. J
 
  • Thread Starter Thread Starter
  • #6
Ich hab mir gerade die Javadoc zu setSoTimeout durchgelesen. Prinzipiell ist es recht interessant, nur das stößt sich an meinem momentanen Design (Client schickt nichts für eine längere Zeit an den Server, ist aber nach wie vor aktiv). Für so etwas wie einen Heartbeat der in zyklischen Abständen erfolgt ist die Methode sicherlich recht interessant da man dann auch darauf reagieren kann wenn die Socket-Verbindung weiterhin offen ist aber sich bsp. der Client in einen Deadlock verfangen hat.

Bzgl. Client: Da könnte ich ein Autoreconnect einbauen das einfach versucht einen Socket erneut aufzumachen. Wenn der Server nicht verfügbar ist weiß dann wenigstens der User das es da etwas hat und ich muss mich Serverseitig nicht weiter drum kümmern. Das muss ich mir mal in Ruhe genauer anschauen.
 
Grundsätzlich wäre mal die Frage wie du momentan die Timeouts erkennst. Außer dem Check über den setSoTimeout würde mir nur die harte Tour einfallen, dass beim Schreibversuch eine Exception fliegt und du entsprechend darauf reagierst. An diesem Punkt könnte man natürlich einen Check einbauen, der einfach einen bestimmten Zeitpunkt wartet und nochmal einen Versuch startet bevor der Socket geschlossen wird. Wahlweise könnte man das ganze auch mit mehrfachem Check machen so dass man z.B. alle 3 Sekunden prüft und das Ganze 10mal versucht und erst bei einem Mißerfolg den Socket zu macht.

MfG
Mr. J
 
  • Thread Starter Thread Starter
  • #8
Thread für Lesen der Message von Socket
[src=java]
public void run() {
this.setThreadName();
while(true) {
String message = "";
try {
message = this.bufferedReader.readLine();
} catch (IOException e) {
this.socketConnection.removeCommunication();
break;
}
if (!message.isEmpty()) {
this.controller.addToMessageQueue(message);
}
}
}
[/src]

Thread zum Schreiben der Messages (Ja mir ist grad bewusst geworden das ich hier nicht bzgl. IOExceptions beim Schreiben in den Socket überprüf, das muss ich noch absichern)
[src=java]
public void run() {
this.setThreadName();
while(true) {
List<String> queueCopy;
synchronized(this.messageQueue) {
if (this.messageQueue.isEmpty()) {
try {
System.out.println(String.format("%s going to sleep", this.threadName));
this.messageQueue.wait();
System.out.println(String.format("%s awakening", this.threadName));
} catch (InterruptedException e) {
// TODO OutgoingSocketCommunication Logmessage für Thread Beenden hinzufügen
System.out.println(String.format("%s was interrupted, going to die...", this.threadName));
break;
}
}
queueCopy = new ArrayList<String>(this.messageQueue);
}
for (String message : queueCopy) {
this.printWriter.println(message);
}
synchronized (this.messageQueue) {
this.messageQueue.removeAll(queueCopy);
}
}
}
[/src]

Sobald mein Lese-Thread eine IOException erhält ruft dieser in meiner SocketConnection eine Methode auf, die den Schreib-Thread interrupted für den Ausstieg aus der Endlosschleife. Jetzt wäre eine Variante, das ich den Write-Thread einfach schlafen schicke und der Input-Thread versucht, 3x alle 10 Sekunden nochmal vom Server zu lesen. Wenn er es schafft kann der Output-Thread weiterarbeiten (sprich wird aufgeweckt), ansonsten interrupte ich den Write-Thread und beende ihn damit. Die andere Variante wäre das ich halt sofort kill und den User dafür verantwortlich mache das er erneut versucht eine Verbindung aufzubauen.
 
Ich glaube ich würde dann die Variante mit mehrfachen Probieren nehmen, sollte allerdings sowohl für Read als auch Write gemacht werden und was ich wesentlich wichtiger finde ist, dass der Server einen solchen Check hat damit dieser nicht irgendwann mit toten Connections überlastet ist.

MfG
Mr. J
 
Zurück
Oben