Meine Frage kurz formuliert:
Wie überprüfe ich ob im Inputstream etwas Neues drinnen ist, damit ich dann .read() im gegenteiligen Fall überspringen kann?
Lang:
Also ich seh im Moment keinen Ausweg: In zb. einem Chat muss ich doch pro Frame einmal nachsehen was im Inputstream geliefert wurde, und das dann auslesen. Nur wird natürlich nicht 1mal pro Frame etwas geliefert, also hängt alles - was völlig inakzeptabel ist.
Jetzt hab ich die Methode availabe() gefunden die mir die Anzahl der Bytes liefern soll, die ich bis zum nächsten Blockieren ungefährdet lesen kann. Aber in der Doku steht: "The available method for class InputStream always returns 0.
This method should be overridden by subclasses. "
Subklassen wie BufferedReader oder InputStreamReader haben aber keine available() Methode. Sagt jetzt bloß nicht dass ich mir selbst einen Reader schreiben muss!
Achja, außerdem dreht mein PC irre auf wenn das Programm gestartet wird, ich hab aber keine rechenintensiven Tasks...das ist auch vom Blockieren nehm ich an oder?
^^ wenn sie Subklassen von InputStream sind erben sie doch alle Methoden von InputStream, also auch aviable().
Es kann höchstens sein das sie es auch nicht richtig überschreiben.
Für den Rest müsstest du ein bisschen Code posten.
Du hast recht, meine Streambeispiele waren gar keine Unterklassen. Aber selbst in den Unterklassen steht in available nur: "This method simply performs in.read() and returns the result."
Was im Endeffekt wieder nicht die gewünschte Funktionalität bietet...
Java:
InputStream is = socket.getInputStream();InputStreamReader isr =newInputStreamReader(is);BufferedReader br =newBufferedReader(isr);OutputStream os = cs.getOutputStream();String line = br.readLine();<--blockiert eeeewig da auf dem anderen PC ebenfalls read() aufgerufen werden muss und ewig blockiert.Das soll einfach übersprungen werden wenn nichts drinnen ist!
starte doch einfach einen eigenen Thread, der immer in.read() aufruft und jedesmal, wenn irgendein Signal geschickt wird, das die aktuelle Nachricht zuende ist, es an irgendwas weiterleitet und dann weiter in.read() aufruft.
Ahh, einen eigenen Thread extra nur für eine Methode praktisch? Ist es gängig sowas zu machen? Wie man merkt bin ich ein Anfänger, deswegen frag ich
Klingt auf jeden Fall sehr gut, danke. Dann gibt mir der Thread praktisch ständig meine Werte zurück, und wenn null/-1/ungültig zurückkommt, beachte ich das einfach nicht. Sooo.. jetzt mal rein ins Vergnügen.
Oft wenn es um Streams geht ist es ratsam solche Dinge auszulagern (außer du willst nur eben eine Datei lesen oder schreiben).
An manchen Punkten bist du sogar gezwugen es so zu machen (Fernsteuern von Konsolenanwendungen mit Input-, Output- und Errorstream).
Ich würde auch sagen, pack es mal in einen eigenen Thread und benutz das Observer-Pattern um Daten an einen verarbeiteten Thread weiterzureichen.
Also die Sache funktioniert überhaupt nicht. Der Chat läuft schon so langsam dass mein PC sich aufhängt (wegen der while(true) schleifen glaub ich, aber ich bekomme nach ner Weile out of heap memory Errors), und Strings werden zwar übertragen, aber nur wenn ich mit dem Debugger Thread für Thread abwechselnd durchlaufe. Die pfuschen sich irgendwie gegenseitig rein. Werden Bytes denn nicht im OutputStream gehalten solange bis ich sie rauslese? Ich stell euch die run() Methoden rein, vielleicht seht ihr nen offensichtlichen Fehler...mann, bei solchen Dingen hinterfrage ich immer ob Programmieren wirklich was für mich ist :/
Java:
/* **SERVER** run() method of the ConnectionManager class, which holds all currently connected sockets.*/while(true){/* This loops through all sockets in "connectionList" and all threads in "readers"
and reads and writes string data from the GUI into/from their streams.*/for(int i =0; i < connectionList.size();++i)try{Socket cs = connectionList.get(i);OutputStream os = cs.getOutputStream();String line = readers.get(i).getString();if(line !=null&&!line.equals("")){byte[] tempBytes = line.getBytes();for(int j =0,counter = i; j < connectionList.size();++j,counter=(counter+1)%connectionList.size()){// write the data from one client, to all the other clients
os.write(tempBytes);}
app.appendString(line);// +display them on server}}
Java:
**CLIENT**while(running){// get string data from the applicationString temp = app.getSentText();if(!temp.equals("\n")&&!temp.equals(""))try{byte[] buffer = temp.getBytes();
os.write(buffer,0, buffer.length);}catch(IOException e){// TODO Auto-generated catch block
e.printStackTrace();}// receive server data and write to client GUIString line = reader.getString();if(line !=null&&!line.equals("")&&!line.equals("\n")){this.app.appendString(line);}}
Eure vorgeschlagene Readerklasse. Jeder Socket hat so einen Readerthread, der ewig läuft und ständig vom Inputstream nachliest.
Java:
publicclassReaderThreadimplementsRunnable{privateSocket sc;privateString readString;publicReaderThread(Socket socket){this.sc = socket;
readString =newString();}publicvoidrun(){while(true){InputStream is;try{
is = sc.getInputStream();InputStreamReader isr =newInputStreamReader(is);BufferedReader br =newBufferedReader(isr);
readString += br.readLine();}catch(IOException e){
e.printStackTrace();}}}publicStringgetString(){// return everything that has been read, and reset the "buffer" string to zero charactersString temp = readString;
readString =newString();return temp;}}
Strings mit dem plus Operator aneinanderzuhängen ist extrem, wirklich extrem Performancefressend sobald es in Schleifen passiert.
€dit: Mach aus readString einen StringBuffer (nicht StringBuilder) und benutz die .append Methode zum dranhängen. StringBuffer ist threadsafe.
Schreib die Zeilen 16 bis 18 außerhalb der unendlichschleife, ansonsten initialisierst du für jede einzelne Zeile die gelesen werden soll immer extra einen neuen BufferedReader und InputStreamReader....
Hab deine Verbesserungen eingebaut, hab aber noch immer 100% Prozessorauslastung. Dafür scheint kein heap overflow mehr zu kommen. Ich kann mir vorstellen warum ich 100% habe, immerhin versucht der Prozessor eine while(true) Schleife sooft wie möglich aufzurufen. Sooft wie möglich ist nunmal immer 100%... gibts eine Alternative zu while(true) und trotzdem einen ständig im Hintergrund laufenden Thread zu haben?
@sparrow:
Die innere for-Schleife fängt bei dem aktuellen Socket an und iteriert solange (auch über das Ende der Liste, deswegen die Modulooperation) bis alle Clients/Sockets einmal angeschrieben wurden mit dem neuen String.
Zb.:
Socket an der Stelle 3 ist aktuell. Liste hat 5 Mitglieder. Dann schicke ich zuerst den String an Socket3, dann 4, 5, dann 1,2 und aus. Insgesamt schicke ich 5 mal. Bisschen allgemeiner gesagt: ich schicke solange bis "j" 5 erreicht hat.
Nein, beide gleich. Anscheinend ist es ein Problem in der GUI, sonst gibts ja keine andere Erklärung. Hab bemerkt dass schon bevor ich den geringsten Netzwerkcode starte, es auf 100% springt. Glaub echt dass die while() Schleifen das Problem sind, denkt ihr nicht? Hier ist wie ich das ganze aufziehe:
Java:
**MAIN Methode**// the user has to define port, host and application type before continuingGUI app =newGUI();newThread(app).start();while(!app.isInitialized());if(app.applicationType.equals("Server"){...}else...
Java:
**ein paar relevante GUI Methoden:**publicvoidrun(){createAndShowGUI();// create labels, buttons, frames..while(true);}publicvoidactionPerformed(ActionEvent arg0){if(arg0.getActionCommand()=="enter"){
sentMessage = textField.getText();
textField.setText("");}}publicStringgetSentText(){// clear it, so we dont send one message possibly twiceString temp = sentMessage +"\n";
sentMessage ="";return temp;}publicvoidappendString(String append){
textareaText = append;
textArea.append(textareaText +"\n");}
Übrigens kann ich denk ich, bestätigen dass die Threads sich gegenseitig stören. Ich hab testweise den übertragenen String nie gelöscht, und einfach immer wieder dranhängen lassen (mit append)...und siehe da: das ganze Fenster war in nem Bruchteil einer Sekunde voll mit meinem Testwort. Nur versteh ich nicht wie sie sich stören können, immerhin wird korrekt in den Outputstream reingeschrieben, das hab ich überprüft.
ajo und in der inneren for Schleife fehlt
Java:
os = connectionList.get(counter).getOutputStream();
Moment, also erstmal:
jede while-Schleife die ungebremst durchläuft verbraucht immer alle Resourcen, da ja nie eine Pause eingelegt wird. Selbst wenn die Schleife leer ist würden alle Resourcen dafür aufgebraucht werden die leere Schleife immer wieder zu durchlaufen.
Ich würde auf dem Server wie folgt vorgehen:
Klasse 1 -> Initialisiert ggf. die GUI und initialisiert und die Sockets aufbaust
Sockets werden, wie du es schon hast, in einer Collection verwaltet. Ich würde hier auf eine HashMap setzen mit dem Benutzernamen als Key. So könnte man auch mal flüstern weil man einfach an den Benutzernamen kommt.
Das wo die Sockets lauschen sollten jeweils einzelne Threads sein. Die verbrauchen ja eben nicht die vielen Resourcen weil sie blockieren bis etwas herein kommt.
So, und dann eben nicht einen Thread immer durchlaufen lassen, sondern eingehende Nachrichten in einer Collection speichern und einen "sendeThread" immer schlafen lassen und bei einer eingehenden Nachricht aufwecken.
Dieser durchsucht dann die Colection mit den Nachrichten und sendet alle verfügbaren Nachrichten an alle Adressaten.
Also warum ich einen extra Sendethread brauche, ist mir nicht klar. Die Nachrichten mal eben in den Outputstream zu schreiben dauert ja nicht lange, und wird sowieso per Knopfdruck, (also bei Druck auf Enter im Textfeld) gemacht bzw. gleich nachdem etwas eingelesen werden konnte.
Und while(true) schleifen brauch ich trotzdem fürs Einlesen von Daten, ein Thread hat ja keinen Rückgabewert. Deswegen muss ich ständig 1mal pro Frame überprüfen ob der Lesethread gerade etwas gelesen hat oder nicht. => resultiert wieder in 100% CPU
Uhm;515892Deswegen muss ich ständig 1mal pro Frame überprüfen ob der Lesethread gerade etwas gelesen hat oder nicht. => resultiert wieder in 100% CPU :([/QUOTE hat gesagt.:
Wieso pro Frame? Programmierst du ein Spiel?
Falls nicht, und du eine SWING-Oberfläche hast, brauchst du nicht jede Sekunde nachschauen.
Wenn du z.B. eine bufferedReader mit .readLine() verwendest blockiert der Thread doch sowieso so lange bis eine vollständige Line vorliegt. Demnach ist es einfacher und schonender für die Resourcen wenn nach einer komplett empfangenen Line dafür sorgt, dass andere Programmteile darüber informiert werden (notice) anstatt ständig zu fragen (polling) ob eventuell neue Informationen vorliegen.
Völlig falsches vorgehen, schau in die FAQ, da ist ein Beispiel für einen Chat, oder denkst du wirklich es ist sinnvoll ununterbrochen zu schauen ob der benutzer was in das textfeld eingibt und das zu senden (dass dann 100x mal das selbe gesendet wird ist dir nicht in den Sinn gekommen?) anstatt auf Befehl (knopf/enter gedrückt) EIN mal den text aus dem feld zu senden?
Einfacher zu programmieren wärs auf jeden Fall, ich nehm an dass man noch irgendwie mit nem Timer einstellen könnte dass nur jede 0.5 Sekunden nachgeschaut werden soll.
Danke Leute, ich hab es dank eurer Hilfe doch tatsächlich noch zum Laufen gebracht. Das Problem was ich jetzt habe ist "nur", dass wenn sich ein Client rausklingt, sich alles aufhängt weil "Connection reset" Exceptions ohne Ende gerufen werden.
Ich kapier nur nicht wo genau das Problem des Programms jetzt ist. Es soll einfach weitermachen ohne diesen Socket. (dazu muss ich jetzt sagen dass ich von Exceptions wenig Ahnung habe... ich weiß nur dass man irgendwie abfangen kann, aber ich klick immer nur auf "generate try and catch" :autsch
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at ue4.ReaderThread.run(ReaderThread.java:46)
at java.lang.Thread.run(Unknown Source)
Und genau das ist das Problem.
Du klickst "einfach nur auf generate xy".
Wie man des öfteren liest lernt man Programmieren nur durch Programmieren. Den Syntax und die Basics (und die Verwendung von Exceptions sind Java-Basics) lernt man nur durch eine einzige Sache: lesen! www.javabuch.de - Das Handbuch der Java-Programmierung