wie angesprochen zu ObjectStreams ruhig auch die Regel anwenden erst out, dann in definieren,
getSocket() vielleicht gar nicht erst anbieten, bevor wer auf falsche Gedanken kommt,
wenn du noch close() am Ende brauchst, dann eine entsprechende Methode
StreamWrapper ist etwas anstrengender Name, Stream selber wäre noch frei und nicht wirklich schlechter,
sonst halt Krämpfe a la verpöntes MySocket, irgendwas Richtung Connection, Client, Server, Handler usw.
die Exception könnte man als RuntimeException weiterwerfen, damit mehr abgebrochen wird,
nur Ausgabe bringt nicht viel, in GUI-Programmen geradezu versteckt, was allerdings auf weitergeworfene Exceptions hin zu einem ActionListener oder so genauso gilt:
Abbruch der Aktion, Exception in Konsole durch Thread
mit kaputten Socket bei Exception wird es später auch andere Fehler bei Zugriff geben
bis auf solche halb-optionalen Kleinigkeiten aber sicher Ziel erreicht
Okay,
immerhin mal doch noch verstanden wie das mit dem Wrapper an sich gemeint war
aaaaber eine Sache noch
adurch umgehst du auch das Problem das möglicherweise der GC zuschlagen könnte da du das Wraper-Objekt und damit dessen Inhalt ja immer irgendwo noch haben musst und somit der GC diese nicht als „frei“ erkennt.
„irgendwo“ habe ich zuerst mal so verstanden, dass ich mir das Objekt in meinem Singleton merke…
aaber Singleton scheinen ja unschön zu sein. Daher hier noch die Frage, wo ich mir das Objekt am besten merke
Edit
Und noch eine Frage.
Gesetz dem Fall, dass der Server mal nicht mehr erreichbar ist. Muss ich ja eine neue Verbindung mit dem Socket aufbauen können. Wäre das jetzt so noch möglich? (Weil bei einer Final-Klasse kann ich ja die werte dann nicht mehr ändern!?)
wo du das Objekt speicherst ist deine Sache, wo hast du bisher den Socket gespeichert um leseNachricht() wiederholt aufzurufen?
ach ja, Singleton, bzw. bei einem Aufbau
ist das MyConnection-Objekt ja eh schon ähnliches, und jedenfalls die ganze Zeit in der run-Methode des Threads,
falls der Thread am Anfang angelegt wurde mit Übergabe und bis Ende der Verbindung läuft, dann ist das ja auch eine dauerhafte Ablage
kann sonst wer aktiv schließen/ beenden? der muss ja auch irgendwie das Gebilde kennen,
irgendeine Ablage hat man immer
kann sonst wer aktiv schließen/ beenden? der muss ja auch irgendwie das Gebilde kennen,
irgendeine Ablage hat man immer
Aktiv kann niemand mein Programm schließen. Wenn ich eine Nachricht mit einem bestimmten Befehl lese, weiß das Programm, dass es sich beenden soll.
Dennoch kann es ja bspw. durch den Putzfraueneffekt passieren, dass die Verbindung zum Server verloren geht.
Das muss ich ja durch mein Programm abfangen (Exceptions weiter werfen).
Aber dann muss ich in einem bestimmten Intervall versuchen die Verbindung wieder auf zu bauen.
Würde bedeuten, ich bräuchte einen neuen Socket bzw. einen komplett neuen Wrapper
oder?
und das Thema „Thread Local“ werde ich mir morgen mal zu gemüte führen.
*** Edit ***
So, noch ein kurzes Update.
Ich bin nun zzt. auf folgendem Stand:
den Wrapper (werde ihn noch umbenennen )
private Socket socket;
private BufferedReader bufferedReader;
private PrintWriter printWriter;
public StreamWrapper(Socket socket) {
try {
this.socket = socket;
this.bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public Socket getSocket() {
return socket;
}
public BufferedReader getBufferedReader() {
return bufferedReader;
}
public PrintWriter getPrintWriter() {
return printWriter;
}
}```
behalte ich in meinem Singleton.
Beim Lesen gehe ich dann hin:
``` BufferedReader bufferedReader = con.getWrapper().getBufferedReader();//new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true) {
if (!con.getWrapper().getSocket().isClosed()) { //getSocket noch entfernen!!
tryToConnectCount = 0;
do {
char[] buffer = new char[4096];
int anzahlZeichen = bufferedReader.read(buffer);
if (anzahlZeichen > 0) {
message.append(new String(buffer, 0, anzahlZeichen));
}
} while (bufferedReader.ready());
logger.info("Nachricht von Fidelio: "+message.toString());
new Thread(new MsgInterpreter(message.toString())).start();
} else if (!con.isClosedRight()) {
//...
} else {
break;
}
} ```
die do..while-Schleife soll hier auch noch entfernt werden. Aber langsam ernährt sich das Eichhörnchen ;-)
und beim Schreiben gehe ich analog vor:
``` printWriter = con.getWrapper().getPrintWriter();
printWriter.print(msg);
printWriter.flush();```
dennoch bekomme ich nach ca. 6 Minuten ein "Connection reset".
Obwohl ich nun ja (soweit ich das sehe) nur noch 2 Stream-Objekte habe, die ich auch immer weiter verwende.
Desweiteren ist mir gerade aufgefallen, dass ich die Exception nur bei dem versuch etwas zu lesen bekomme.
Wenn ich nämlich nach der Exception mein Programm weiter laufen lasse und es dann wieder eine Dummy-Nachricht an den Server sendet, bekomme ich keine Exception (obwohl ich diese an der Stelle erwartet hätte...)
hat hier jemand noch eine Idee?
Gut, davon ausgehend das nun die gemachten Vorschläge soweit umgesetzt hast um zumindest den logischen Aufbau zu korrigieren (auch wenn mir dieses MyConnection-Singleton so überhaupt nicht zusagt) und weiterhin Exceptions beim read() fliegen kann es nur noch daran liegen das entweder der Stream/Socket zusammenbricht (kann man prüfen in dem man nach der Exception einfach weiter versucht die Verbindung noch zu nutzen, wenn sie tot ist dann bekommt man das auch mit) oder beim read() irgendwas nicht ganz stimmt. Ein Timeout würde ich ausschließen da dies dann auch mit einer entsprechenden Timeout-Meldung kenntlich gemacht wird und nicht mit Con-Reset. Auf jeden Fall solltest du mal mit Wireshark prüfen was da so übers Netz geht, nicht das da noch irgendwelche Pakete rumschwirren die zu dem Fehler führen könnten (gibt da so einiges).
Alternativ kann es immer noch der Server sein, müsste man einfach mal wie schon erwähnt wurde als gegenprobe mit nem eigenen Server versuchen ob dabei ähnliches auftritt. Wenn dies nicht der Fall sein sollte lässt sich der Fehler auf den Server eingrenzen, sollte aber auch beim Alternativ-Test Fehler auftreten wird die Liste der möglichen Ursachen schon wieder etwas länger. Laut dem was Google so dazu ausspuckt kann es ein simpler Software-Bug im TCP-Stack deines OS sein oder sogar bishin zu einem Hardware-Fehler.
Es ist schwierig bei solchen Probleme eindeutig zu klären woran es nun liegt, vor allem wenn man keine Kenntnis oder gar Kontrolle über die Gegenseite hat (was man ja mit dem schon angesprochenen Selbst-Test umgehen kann).
Klingt auf jeden Fall alles sehr Merkwürdig und auch mich würde der wahre Grund interessieren, weil genau solche Probleme in großen Maßstäben immer wieder zu Ausfällen in Millionenhöhe führen. Du machst es da schon besser und versuchst zumindest von dir aus den Fehler zu finden und zu beheben. Alleine das machen schon nur ganz wenige weil man sich denkt : wir schrauben die Parameter einfach so weit runter das es nich mehr auftritt.
Zu dem “Verbindung geht verloren” :
Nur weil eine Klasse final ist heißt das nicht das du nicht mehrere Instanzen davon erzeugen kannst, sondern lediglich das sie nicht mehr erweitert werden kann.
KeepAlive-Ping/Pong auf Protokoll-Ebene ist zwar lästig und sollte auch eigentlich durch das Grundkonzept TCP nicht nötig sein, aber wie ironie an “gut” ironie aus es funktioniert erlebt man ja immer wieder.
Netzwerk-Programmierung ist schon ein nettes Thema das mich sehr interessiert, obwohl auch ich gerade mal an der Spitze des Eisberges kratze … hust nio und so hust.
Werde dann wohl nicht drum rum kommen, mir mal einen eigenen Server zu schreiben. Zuerst werde ich aber auch noch mit Wireshark prüfen was da so über das Netz geht.
Wobei, wie in meinem letzten Post ja schon beschrieben, der Socket / die Verbindung kann weiter genutzt werden, da ein erneutes schreiben (nachdem die Exception geworfen wurde) nicht fehlschlägt. (ein erneutes lesen jedoch schon).
Ich habe mir bei der Exception interessehalber noch die Status des Socket angeschaut.
socket.isBound() = true;
socket.isClosed() = false;
socket.isConnected() = true;
socket.isInputShutdown() = false;
socket.isOutputShutdown() = false;
Also die Angaben des Socket sehen i.O. aus.
Nur weil eine Klasse final ist heißt das nicht das du nicht mehrere Instanzen davon erzeugen kannst, sondern lediglich das sie nicht mehr erweitert werden kann.
Stimmt. War gestern wohl doch zu verwirrt/genervt von diesem Fehler das ich dass nicht mehr verstanden habe
der InputStream auf deiner Seite (Output auf der anderen, wobei nicht nachprüfbar) ist sicherlich geschlossen, diese beiden Verbindungen gehen irgendwie einzeln
edit: was wohl ‚isInputShutdown() false‘ entgegensteht dann eben nicht
ich hatte ja schon fast gehofft, dass ich bei „isInputShutdown“ ein true bekomme. Dann hätte ich zumindest einen Anhaltspunkt gehabt…aber neeeeein ^.^
ich habe mir jetzt mal eine Methode „reconnect“ in mein Singleton gebastelt. Diese Methode setzt dann eigentlich alles zurück und erzeugt einen neuen Wrapper und somit ja eine neue Verbindung.
this.KeepAliveThread.interrupt();
try {
this.getWrapper().getIn().close();
this.getWrapper().getPrintWriter().close();
this.getWrapper().getSocket().close();
setConnected(false);
init();
} catch (IOException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
}```
``` private void init() throws UnknownHostException, IOException, JAXBException {
this.setSocket(new Socket(tcpIp, tcpPort));
setWrapper(new StreamWrapper(this.socket));
//...
}```
In meinem ersten Test schien es zu funktionieren.
Mal sehen was passiert wenn ich es länger durch laufen lasse und keinen Breakpoint habe.
Aber so kann es doch zu Datenverlust kommen oder?
Weil wenn beim "read" dieser Fehler kommt...kann es dann nicht sein, dass ich nicht mehr alle Daten erhalte?
war der Fehler nicht bisher bei Inaktivität statt mitten im Lesen?
aber das ist eh alles immer wilderes Raten hier
wenn die Umstände es erlauben kannst du ja doch für jede Anfrage frisch neu verbinden
oder zumindest nach Pause von X Minuten bei neuer Anfrage erstmal neu verbinden
war der Fehler nicht bisher bei Inaktivität statt mitten im Lesen?
Nochmal kurz das Fehlerszenario:
Client sendet an Server die initiale Nachricht
Server meldet „alles klar, du kannst Anfragen senden“
Client sendet, Server antwortet
5 Minuten passiert nichts
Client sendet an Server „Ist noch alles I.O.?“ (Dummy-Nachricht)
Server scheint etwas zurück zu geben → Exception
ob und was der Server zurück gibt weiß ich ja leider nicht, weil ich ja direkt die Exception bekomme.
Ich habe ja die Zeile: int anzahlZeichen = bufferedReader.read(buffer);
an der der Client auf Nachricht vom Server wartet. Und nachdem der Client „alles I.O.?“ An den Server gesendet hat, fliegt an dieser Stelle die Exception.
Gut, halten wir fest : du bekommst beim InputStream.read() eine SocketException mit der Message “Connection reset”. Mal eben in den Source geguckt wird diese ausgelöst wenn der native FileDescriptor (irgendwo in den C-tiefen der VM-implementierung) entweder eine ConnectionResetException auslöst oder bei der Prüfung auf isConnectionReset() mit true antwortet. Ich habe mich jetzt nicht weiter in den Source gegraben aber gehe von aus das es letztlich irgendein System-Call sein dürfte. Wenn man sich mal verdeutlicht wie weit abstrakt eine Java-VM eigentlich von der Hardware ist dürfte klar sein wie viele Schichten es bis zum IP-Stack des OS gibt und wie ewig lang der gesamte Call-Stack wird. Gehen wir die Frage logisch an kann es eigentlich nur zwei Möglichkeiten geben : entweder wird halt vom Server die Verbindung geschlossen und es kommt ein FIN-Packet (kann man ja mit WireShark testen) oder es tritt (warum auch immer) ein Bug im IP-Stack des OS auf der dann bis nach oben zum Java-Code in diesem “Connection Reset” endet. Erlich gesagt hoffe ich das es “nur” der Server ist der die Verbindung kappt, ansonsten würde meiner Erfahrung nach eigentlich nur noch ein Hardware-Defekt übrig bleiben, oder halt ein wirklich extrem mieser Software-Bug.
Auch könnte man mal versuchen deinen Client-Code auf einem anderen Rechner mit einem anderen OS zu testen um halt deinen aktuellen Rechner bzw dessen OS als mögliche Fehlerquelle auszuschließen. Alles in allem wirklich ein sehr merkwürdiges Problem bei dem die Tatsache das der Server dir gegenüber eine BlackBox ist nicht wirklich weiterhilft.
— EDIT —
Gerade auf den letzten Post : wenn bei InputStream.read() eine Exception fliegt ist der Stream in der Regel zumindest für dich für die weitere verwendung geschlossen > du müsstest also einen neuen erzeugen. Man könnte versuchen ob Socket.getInputStream() gleiches Ergebnis liefert > wenn ja dann ist der Socket dicht und du musst so oder so reconnecten.
Sooo
ich habe nun mal Wireshark angeschmissen und versucht hier was zu finden.
Und ich habe auch etwas gefunden. (Jetzt muss ich nur noch genau verstehen, was ich gefunden habe :-D)
Also,
nachdem der Client “alles i.O.?” sendet
sehe ich bei Wireshark, dass ich ein [RST] zurück bekomme.
Sehe ich es richtig, dass ich also doch vom Server ein Reset-Paket erhalte? Oder kann das nun auch noch andere Ursachen haben?
Das hast du schon korrekt verstanden : RST bedeutet RESET > der Server sendet dir ein Packet mit der Steuer-Information das er die Verbindung zurückgesetzt hat.
Wikipedia sagt zum RST-Flag : Gegenstelle meldet das Verbindung abgebrochen werden soll / wurde.
Folgerung : der Server beendet (warum auch immer) von seiner Seite aus die Verbindung und zwingt dich dadurch zu einem kompletten Re-Connect.
Jetzt kommt das ins Spiel was ich schon ganz am Anfang gesagt habe : auf die DOC das der Server angeblich von sich aus die Verbindung nie trennen würde kannst du dich nicht verlassen > jetzt haben wir den Beweis dafür.
Ich würde demnach erstmal beim Server-Entwickler nachfragen warum denn ein RST kommt obwohl dies laut DOC eigentlich ausgeschlossen sein sollte. Ich vermute hier mal ganz sporadisch irgendeinen Fehler im Code des Servers der dann z.B. eine Exception auslöst die nicht korrekt behandelt wird und daher der Socket zusammenbricht.
So
pünktlich zum Freitag konnten nun alle Probleme erkannt und behoben werden.
Es war kein Fehler in meinem Programm :laola:
Folgerung : der Server beendet (warum auch immer) von seiner Seite aus die Verbindung und zwingt dich dadurch zu einem kompletten Re-Connect.
war leider auch nicht korrekt…auch wenn es lange Zeit danach aussah :reflect:
Das es nicht der Server ist, habe ich dadurch herausbekommen das ich mit dem Entwickler der Gegenseite gesprochen habe und wir gemeinsam einen Test durchgeführt und unsere Netzwerk-Snippets ausgetauscht haben. Daraus resultierte, dass deren Server nie ein RST gesendet hat.
Und genau da lag der Hund begraben…
Der Router kappte nach 5 Minuten ohne Kommunikation die verbindung. In Wireshark sah es aber dennoch so aus, als würde der Server das RST senden.
Alles in allem bin ich froh, dass das Problem nun geklärt ist.
Es hatte auch sein gutes, weil ich so das Programm an sich verbessern konnte und auch das ein oder andere dabei gelernt hab
Ja, das sind so Dinge die jeder kennt und eigentlich auch jeder weis das es sie gibt und das es so abläuft, aber an sowas denkt bei so nem Fehler keiner.
Was mich sehr viel mehr verwundert ist das ein RST vom Router kommt, und vor allem dann auch nur in eine Richtung. Wenn der Router schon vorhat die TCP-Verbindung nach einem Timeout zu killen dann sollte er sinnvoller Weise dies auch an beide Seiten machen, und nicht nur ins LAN. Warum ? Relativ einfach erklärt : wenn der Router nur dem Rechner im LAN und damit deinem Client ein RST schickt dann bricht dieser die Verbindung halt ab ohne noch weitere Aufräumarbeiten erledigen zu können (die wenn überhaupt mit sicherheit auch irgendwo am Router hängen bleiben würden). Der Server weis davon aber nichts weil er ja nicht darüber informiert wird. Also ist für ihn die Verbindung immer noch gültig und offen. Ein Abbruch dürfte wenn dann nur nach einem Timeout oder dem Fehler folgen dir eine Nachricht zu senden.
Alles in allem würde ich mich diesbezüglich mal versuchen an den Router-Hersteller zu wenden und wenn möglich ggf mal testweise einen anderen ausprobieren.
Trotzdem interessant letzten Endes zu wissen was wirklich Ursache war.