Java Schleife und Bytestream - nächster Durchlauf erst nach auslesem des Streams

palaber

Captain
Registriert
Juni 2006
Beiträge
3.856
He Hallo nochmal,

gleicher Tag neues Problem. Und zwar hab ich eine Schleife laufen, die mir Daten aus nem Stream holt und in diesen schreibt. Das geht soweit auch. Nur nicht richtig^^
Jetzt zum konkreten Problem. Ich habe einen Server, der schickt eine Nachricht an meinen Client. Wenn die Nachricht im Client angekommen ist soll vom Client an den Server eine Nachricht gesendet werden. Diese wird vom Server empfangen.

Erst wenn diese Nachricht im Server ist, soll der Server die Schleife neu durchlaufen. Beim Server klappt das, beim Client irgendwie nicht. Und ich komm da momentan nicht weiter. Hab ich irgendwo nen Denkfehler? Hier mal der betreffende Code:

Client Schleife:
Code:
while(bRun)
	{
    	boolean sendOk = btClient.getStream();
        
    	if(sendOk)
        	{
         	String sTmp = btClient.getSOut();
       		threadComHandler.obtainMessage(1, sTmp).sendToTarget();        			
                counterEmpfangen++;
        			
        	boolean bGesendet = btClient.sendStream("0");
        	if(bGesendet)
        	{
        	         counterGesendet++;
        	}
       	}
        		
       	else
       	{
       		cancelConnect();
       	}
        		
       	counterGesamt++;
}

Dann noch die beiden aufgerufen Methoden:
Code:
public boolean getStream()
{
	int iAnzahlBytes;
	byte[] inByteArray = new byte[1024];

	try 
	{
		iAnzahlBytes = mmInStream.read(inByteArray);
	
		if (iAnzahlBytes > 0)
		{
			sOut = new String(inByteArray);
			return true;
		}
		
		return false;
	}
	
	catch(IOException e)
	{
		sOut = e.toString();
		return false;
	}
}

Code:
public boolean sendStream(String s)
	{
		byte[] outByteArray = new byte[1];
		
		try
		{
			outByteArray = s.getBytes();
			mmOutStream.write(outByteArray);
			mmOutStream.flush();

			return true;
		}
		
		catch(IOException e) {return false;}	
	}

Irgendwie läuft die Schleife aufm Client öferts - es wird öfters eine Nachricht gesendet als empfangen. Eigenlich sollte immer eine Nachricht empfangen werden und dann gesendet werden.
 
Was soll das ganze am Ende ergeben? Vielleicht würde JMS ein bessere Ansatz sein als es selbst zu schreiben.
Ansonsten wenn deine Angaben passen dann gibt getStream true zurück. Was steht den in der Message? Ist die Leer oder ist was drin und wenn ja passt die Message und ist Valid?

Abseits davon würde ich eher
while (btClient.getStream())

Das wäre zumindest von der Schreibweise eleganter. Abseits davon scheint irgendwas auf dem Socket vom Server geschrieben werden. Schau mal wann sendStream aufgerufen wird und von wem, vielleicht siehst du da den Fehler.

MfG
 
Mal abgesehen davon, dass z.B. unnötige Instanzen erzeugt werden, sind da mindestens zwei Denkfehler drin. Funktionieren kann das nur, wenn der Server genau wie der Client in Deinem Code auch immer nur ein Byte sendet und dann auf eine Antwort wartet.

Sendet der Server in seiner Nachricht aber mehr als 1 Byte, ist es Glücksache, ob Dein Code so funktioniert wie gedacht. Sendet er mehr als 1024 Byte pro Nachricht, wird es ganz sicher nicht funktionieren wie gedacht. Der Fehler liegt in der Methode getStream(), weil Du Dich darauf verlässt, dass

a) niemals mehr als 1024 Byte vom Server gesendet wurden
b) Du davon ausgehst, wenn der Server X Bytes gesendet hat (mit x < 1024), dir das mmInStream.read(inByteArray) auch X zurückliefert.

Ob Annahme a) stimmt hast Du mit Deinem Servercode ja selbst in der Hand, Annahme b) ist aber eben nicht immer zutreffend. eigentlich müsstest Du mit einer while-Schleife im getStream() lesen.

Da die Endpunkte aber offensichtlich die ganze Zeit connected sind, werden die Streams auch nicht geschlossen. Hier kommt der zweite Denkfehler. Du wirst beim Lesen von dem Stream ja niemals ein EOF sprich einen Rückgabewert von -1 beim read() erhalten, solange der Stream nicht geschlossen wurde.
Eine while-Schleife im getStream() würde Dir also auch nicht helfen, da sie blockieren würde, wenn gerade keine weiteren Daten gelesen werden können. Du weißt also gar nicht, ob die Nachricht vom Server schon vollständig war oder Du bis zum Sankt-Nimmerleins-Tag auf den Rest wartest, weil er niemals kommt.

Was Du da versuchst, ist einfach kein deterministisches Protokoll. Du müsstest z.B. ein bestimmtes Byte in der Nachricht definieren, anhand der Du das Ende einer Nachricht erkennen kannst (War das jetzt alles oder kommt da noch was ?), dann könntest Du das mit einer while()-Schleife im getStream() programmieren. Ansonsten ist das Funktionieren/Nicht-Funktionieren vom Timing oder z.B. davon ob Du Buffered-Streams verwendest oder nicht, abhängig.

Kleiner Tip am Rande, der anderen hilft den Code besser lesen zu können und Dir auch, weil Du in ein paar Monaten deinen Code auch nicht mehr im Kopf haben wirst. Achte mal ein wenig auf die Semantik der Bezeichner, benenne Variablen und Methoden so, dass es auch dem entspricht, was sie tun.

Z.B.: getStream() liefert keinen Stream, es liest von einem Stream in eine Member-Variable. readFromStream() wäre also passender.
Das Ergebnis von getStream() weist Du einer Variablen "sendOk" zu. Du bist aber auf dem Client, und was da OK war, war nicht das Senden sondern der Empfang. "receivedOk" wäre wohl passender. Beim darauffolgenden Versand der Daten vom Client zum Server mit sendStream() - auch hier wäre writeToStream() - ist der Variable ja schon etwas passender "bGesendet" benannt.

Allerdings ist so ein Mischmasch von Deutsch / Englisch ziemlich verwirrend, gewöhne Dir am Besten gleich nur Englisch an. Und wenn Du solche Präfixe wie "s" für einen String oder "b" für einen boolean verwendest, dann mache das doch bitte durchgängig. Bei Member-Variablen ist ein "m_" Präfix auch ganz nützlich, auch ohne Syntax-Highlighting sieht man dann sofort, was ein Mmeber und was eine lokale Variable ist. Desweiteren sind Naming-Clashes zwischen Membern und lokalen Variablen ,mit einer der häufigsten Gründe für total überflüssige Bugs.
 
Anmerkung zum Code-Style:
* Wie schon von HamHeRo gesagt, bitte kein sprach-mischmasch. Bevorzugt englisch, notfalls deutsch, in jedem Fall konsistent
* Typenabkürzungen wie b für boolean oder s für String sind nicht mehr üblich. Das war Code Style aus den 90ern, wo die IDEs noch nicht so stark vertreten und mächtig waren wie heute.
* Bitte eindeutige Methodennamen wählen. Jeder eindeutige und prägnanter Name spart am Ende Kommentare im Code. Der zukunfts-Palaber wird es dir danken. ^^
 
Hey danke euch! Ich werde mir das mit dem Stream mal anschauen. Es geht bei der Sache nur mal darum zu testen, wieviel Nachrichten ich denn von nem Server an meinen Client schicken kann. Is nur so ne spielerei, die mich eben interessiert!

Danke das ihr mir zur Semantik noch ein paar Tipps gebt. Mir fehlt einfach die Übung beim programmieren und habe selten (konstruktive) Kritik zum Code bekommen. Ich werde versuchen die Dinge in Zukunft umzusetzen. Habt ja recht, dass es so schon Sinn macht.

Ne Frage, vielleicht is sie doof, hab ich noch zu Streams:
Momentan habe ich ein Bytearray mit 1024 Byte das ich übertrage. Es ist also komplett "voll".
Wenn ich jetzt im Client eine While-Schleife habe, die mir 1024 Byte ausließt, ich haber nur 512 Zeichen übertrage, was passiert dann? Also wird immer das komplette Array in den Stream geschrieben oder nur die Elemente die nicht "null" oder was auch immer sind?!?
 
Zuletzt bearbeitet:
palaber schrieb:
Ne Frage, vielleicht is sie doof, hab ich noch zu Streams:
Momentan habe ich ein Bytearray mit 1024 Byte das ich übertrage. Es ist also komplett "voll".
Wenn ich jetzt im Client eine While-Schleife habe, die mir 1024 Byte ausließt, ich haber nur 512 Zeichen übertrage, was passiert dann? Also wird immer das komplette Array in den Stream geschrieben oder nur die Elemente die nicht "null" oder was auch immer sind?!?

Wieviel Bytes du Client-seitig aus dem Stream an einem Stück auslesen kannst, hat überhaupt nichts damit zu tun, wieviel Bytes serverseitig in einem Rutsch geschrieben wurden. Dur kannst Servers-setig doch mit einer ganz anderen Programmiersprache/mit einem anderen OS etc. was auf dem Socket schreiben. Die Streams auf dem Client wissen doch nichts von denen auf dem Server.

Wenn ich Dich richtig verstehe, dann schreibt der Server als Nachricht immer exakt 1024 Bytes und dann wartet er auf den Client, der als Antwort 1 Byte senden soll ?
Dass der Code dann auf dem Server funktioniert ist klar, ich schrieb ja schon eingangs, dass dein Code nur zuverlässig funktioniert, wenn die geschrieben Nachrichten nicht länger als 1 Byte sind, sonst ist es Glücksache.

Das mit den String-Instanzen ist nicht so optimal, ich belasse es aber mal bei Deiner Schreibweise, damit Du die Unterschiede siehst.

Clientseitig müsste der Code in Deinem getStream() so aussehen (Definition: Eine komplette Servernachricht besteht immer aus exakt 1024 Bytes):

Code:
...
int numberOfPendingBytesToRead = 1024;
sOut = null;

try {
    while ( numberOfPendingBytesToRead > 0 ) {
        Arrays.fill(inByteArray, 0); // purge data from previous read
        int numberOfBytesRead = mmInStream.read(inByteArray);
        if ( numberOfBytesRead > 0 ) {
            if ( null != sOut )
                sOut.concat( new String(inByteArray) );
            else
                sOut = new String(inByteArray);
            numberOfPendingBytesToRead -= numberOfBytesRead;
        } else {
            // Stream was closed before complete message was read
            sOut = null;
            return false;
        }
    }

    if ( numberOfPendingBytesToRead < 0 ) {
        // Server message was longer than 1024 Bytes, something went wrong on server side
        sOut = null;
        return false;
    }

    return true;
} catch ( IOException e ) {
    sOut = null;
    return false;
}
...

Was mit den restlichen Bytes im Array passiert, wenn das Array nicht komplett gefüllt wurde, steht doch in der Doku:

The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], leaving elements b[k] through b[b.length-1] unaffected.

Wie der String aus einem nicht komplett gefüllten Array erzeugt wird, hängt vom gewählten Charset/Encoding ab. Mit UTF-8/UTF-16 klappt das so wie oben beschrieben, wenn ich mich nicht irgendwo vertippt habe. Ich habe das einfach mal so frei runtergeschrieben.
 
Zuletzt bearbeitet:
Eine Erweiterung zu dem was HamHeRo richtigerweise schreibt wäre das du dir eine Protokoll ausdenkst wenn eine länge kleiner gleich 1024 nicht immer gegeben ist. Du wartest solange bis die Repsonse complete ist was mit einer RegExp überprüfbar ist.

Mal ein Beispiel:
Login Response könnte dann 200 - OK sein, für eine Fehler wäre dann sowas wie 401 - Not Found drin beendet mit \r\n. Dann weißt du es gibt einen Return Code, eine Message bzw. ggf. die Daten und Abgeschlossen durch eine Carriage Return + Line feed.

Was jedoch nicht berücksichtigt wird ist wenn der Server mehrere Nachrichten gleichzeitig an den selben Client schickt was für mich nicht ersichtlich ist ob es den Fall geben kann oder nicht. Das kann auch zu seltsamen Effekten führen.

 
Hey, erstmal vielen Dank. Bin erst jetzt wieder dazu gekommen weiter zu machen.

Mein Ziel hat sich jetzt doch wieder etwas verändert. Also vom Server kommt eine Nachricht. Diese kann zwischen 1 Byte und 1024 Byte groß sein. Kurz zur Warnung - ich bin kein guter Programmierer und blicke Streams irgendwie nicht so ganz. Also bitte nicht böse sein, wenns ne bescheuerte Frage is ;)

Der Code von HamHeRo gibt ja an, wieviel Byte aus dem Stream gelesen werden sollen:
Code:
int numberOfPendingBytesToRead = 1024;

könnte man nicht einfach die Bytes im Stream zählen - dachte eigentlich das macht:
Code:
int numberOfPendingBytesToRead = mmInStream.read(inByteArray);

Dann würde ja die schleife maximal solange laufen, wie es bytes zum auslesen gibt. Oder kann es sein,
dass nicht alle Bytes des Streams durch mmInStream.read() gelesen werden? Und dann anstatt 1024 nur 1000 ausgelesen werden und mit dem zweiten read() dann die restlichen 24?!

Hat jemand von euch nen gutes Tutorial zu Streams? Wäre glaub ganz gut da erstmal richtig zu lesen. Nix mit Methoden, sonder die Grundlagen...
 
palaber schrieb:
...
könnte man nicht einfach die Bytes im Stream zählen - dachte eigentlich das macht:
Code:
int numberOfPendingBytesToRead = mmInStream.read(inByteArray);

Dann würde ja die schleife maximal solange laufen, wie es bytes zum auslesen gibt. Oder kann es sein,
dass nicht alle Bytes des Streams durch mmInStream.read() gelesen werden? Und dann anstatt 1024 nur 1000 ausgelesen werden und mit dem zweiten read() dann die restlichen 24?!

Nein, das würde überhaupt nicht funkitionieren. Das hatte ich doch oben geschrieben. Wie soll denn die Client-Stream-Implementierung wissen, ob nach einen Read von X Bytes jetzt Sendepause ist, oder da noch Y Bytes hinterherkommen, wenn die Gesamtanzahl der Bytes nicht bekannt ist ?

Hat jemand von euch nen gutes Tutorial zu Streams? Wäre glaub ganz gut da erstmal richtig zu lesen. Nix mit Methoden, sonder die Grundlagen...

Die Java Doku, die hatte ich jua schon oben zitiert. Da steht klipp und klar drin: Ein read() blockiert so lange bis Daten ankommen, der Stream von Serverseite geschlossen wurde oder wartet bis zum St-Nimmerleinstag in der Hoffnung, dass noch etwas kommt. Wenn Du eine bestimmte Anzahl X Bytes lesen möchtest, dann werden "at most = bis zu" X Bytes gelsen. Der Rückgabewert bestimmt wieviele tatsächlich gelesen wurden, dass kann zwischen 1 und X sein. Ist der Rückgabewert kleiner X, dann muss Du es nochmal versuchen.

Wenn eine variable Anzahl Bytes vom Server schreibst, dann gibt es exakt, und damit meine ich exakt, zwei Möglichkeiten:

1. Der Server schickt in den ersten Bytes eine Längenangabe mit, so dass der Client genau weiss, wieviele Bytes noch zulesen sind, bis die Nachricht abgeschlossen ist
2. Die Nachricht vom Server wird durch eine bestimmte Bytefolge terminiert, an der der Client erkennen kann das die Nachricht nun komplett gelesen wurde

Andere Möglichkeiten gibt es nicht. Der Client muss erkennen können, wann die Nachricht fertig gelesen wurde.

Das ist nicht wie bei Strings, die Du in einer Methode mit Daten füllst und dann an anderer Stelle mit mit length ermittelt kannst, wieviel da drin steht. Das Ganze ist doch asynchron. Die Stream auf Clientseite und Serverseite sind doch vollkommen entkoppelt, die wissen nix voneinander. Der Client kann nur wissen, wie lang eine Nachricht ist, wenn der Server diese Information mit übermittelt, oder wenn wenn die Länge fix definiert ist.
 
Zurück
Oben