ByteArray über Netzwerk senden klappt nicht

Klösp

Aktives Mitglied
Hallo Java-Forum.

Ich möchte ein Objekt in ein Byte[] speichern und dann an den Server schicken.


Java:
import java.io.Serializable;

public class Person implements Serializable {
	
	private String name, vorname;
	private int alter;
	
	public Person(String name, String vorname, int alter) {
		super();
		this.name = name;
		this.vorname = vorname;
		this.alter = alter;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", vorname=" + vorname + ", alter="
				+ alter + "]";
	}

}


Client:
Java:
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;


public class Client {

	public static void main(String[] args) throws IOException {

		Socket server = null;

		try {
			server = new Socket("localhost", 8080);
			Person p = new Person("Gause", "Gundula", 42);
			ByteArrayOutputStream baos=new ByteArrayOutputStream();
			ObjectOutputStream os = new ObjectOutputStream(baos);
			os.writeObject(p);
			os.flush();
			byte[] ba=baos.toByteArray();
			baos.close();
			
			ObjectOutputStream oss = new ObjectOutputStream(server.getOutputStream());
			oss.write(ba);
			oss.flush();
			
			BufferedReader br=new BufferedReader(new InputStreamReader(server.getInputStream()));
			System.out.println(br.readLine());			
		} finally {
			if (server != null) {
				server.close();
				
			}
		}
	}
}

Server:
Java:
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
public class Server {
 
    public static void main(String[] args) throws IOException {
 
        ServerSocket server = new ServerSocket(8080);
 
        while (true) {
            Socket client = null;
            try {
                client = server.accept();
                System.out.println("verbindung aufgebaut");
                doSomething(client);
 
            } finally {
                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
 
    private static void doSomething(Socket client) throws IOException {
 
     InputStream is=client.getInputStream();
     
     PrintWriter pw=new PrintWriter(client.getOutputStream());
     byte[] pb=new byte[1024];
     int i;
    try {

    	while((i=is.read(pb))!=-1){
    		System.out.println("Write "+i+" Bytes.");
    	}
    	 ByteArrayInputStream bis = new ByteArrayInputStream (pb);
         ObjectInputStream ois = new ObjectInputStream (bis);
         Person p= (Person) ois.readObject();
         
		System.out.println(p.toString());
		pw.write("Erfolg \n");
		pw.flush();
		
	} catch (ClassNotFoundException e) {
	
		e.printStackTrace();
	}
    finally{
    	is.close();
    	pw.close();
    }
        
    }
}

Irgendwo hängt es noch.

Beim Server bekomme ich folgenden Ausgabe:
verbindung aufgebaut
Write 4 Bytes.
Write 112 Bytes.

Also wird ja scheinbar etwas übertragen und auch ins Byte[] geschrieben.
Das Problem ist denke ich dann bei zurück wandeln in ein Object.

Kann jemand helfen?

Danke im Vorraus
 
Hi,

ich denke, dass der eigentliche Fehler hier liegt:
while((i=is.read(pb))!=-1)

is.read(pb) liest in das bytearray pb und zwar immer von Anfang an. Da die Daten des Servers nach und nach ankommen (Du hast ja mehrere Zeilen mit dem Write...), überschreibst Du die Daten immer selbst. Das wäre zu fixen über eine Erfassung der aktuellen Position und der Nutzung der read Datei, die auch die Position (Offset) und Länger (also Länge Array - Position) mitnimmt.

Aber das ist alles meiner Meinung nach so gar nicht notwendig. Du kannst den ObjectStream doch immer direkt auf den Stream vom Socket setzen. Oder habe ich da etwas verpasst, das eben dies verhindert?

Mit den besten Grüßen,

Konrad
 
Wie der Code aussehen könnte habe ich jetzt einmal schnell für Dich ausprobiert. Dies wird mein erstes Posten von Code, daher bitte Geduld haben, falls es nicht gleich ganz so von Anfang an klappen sollte. (Und um es kurz zu halten habe ich nur die main Funktion vom Client und die doSomething Funktion vom Server kopiert.)

Client - main:
Java:
        public static void main(String[] args) throws IOException {

            Socket server = null;

            try {
                server = new Socket("localhost", 8080);
                Person p = new Person("Gause", "Gundula", 42);
                ObjectOutputStream os = new ObjectOutputStream(server.getOutputStream());
                os.writeObject(p);
                os.flush();
                BufferedReader br=new BufferedReader(new InputStreamReader(server.getInputStream()));
                System.out.println(br.readLine());
            } finally {
                if (server != null) {
                    server.close();

                }
            }
        }

Server-doSomething:
Java:
        private static void doSomething(Socket client) throws IOException {
    
         InputStream is=client.getInputStream();
    
         PrintWriter pw=new PrintWriter(client.getOutputStream());
        try {
    
             ObjectInputStream ois = new ObjectInputStream (is);
             Person p= (Person) ois.readObject();

            System.out.println(p.toString());
            pw.write("Erfolg \n");
            pw.flush();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        finally{
            is.close();
            pw.close();
        }

        }

Aber damit habe ich jetzt natürlich nicht den eigentlichen Fehler in Deinem Code verbessert sondern nur die Applikation zum Laufen gebracht. Du solltest aber auch ruhig einmal meinen Hinweis probieren. Vom Code her wäre es in etwa folgendes (direkt im Form geschrieben und ungeprüft):

Java:
int pos = 0;
int size = 1024;
byte[] pb=new byte[1024];
int bytesRead = 0;
while((bytesRead=is.read(pb, pos, size-pos))!=-1){
   pos +=bytesRead;
   System.out.println("Read "+bytesRead +" Bytes.");
}
Be aware that this solution will simply be stuck if you receive more data than fit into the buffer. You will fill up the buffer and then loop with reading 0 bytes over and over!

And while thinking about this solution: The loop will run as long as the the stream is open. The Server is not closing the connection so your application simply hangs with reading 0 bytes. So this is not a good solution.

You always need to implement a protocoll that knows when some part is read. So for example in my IRC application I had a thread that was receiving data and stores it. Whenever I received data I checked for a newline (which was a separator in the IRC protocoll) and then evaluated the message. The objectStream is telling on the stream what the size of the object is, so it can request that number of bytes from the stream.

I hope this was helpful.

With kind regards,

Konrad
 
Hallo Konrad,

vielen Dank erstmal.
Ich hätte dazu schreiben sollen, dass es sich hier nur um einen Test handelt. Also ohne großen Sinn dahinter.
Bisher hatte ich immer nur Strings hin und her geschickt und ich wollte einfach mal testen wie das mit anderen Objekten funktioniert.
Die erste Lösung hatte ich davor auch so in etwa stehen und die funktioniert auch.
Nun wollte ich aber eben ausprobieren, wie es funktionieren könnte wenn man das in Form eines Byte[] bekommt.

Da war halt eben mein Ansatz:
1.byte Array empfangen
2. wieder in Object wandeln

Den Code mit der verbesserten Schleife hab ich ausprobiert. Das Ergebnis ist leider das selbe wie voher auch.

Oder gibt es noch einen anderen Ansatz das zu machen (für den Fall das man ein Byte[] bekommt)

Vielen Dank
 
Hallo,

das eigentliche Problem dürfte wirklich sein, dass die Schleife so lange läuft, bis die Verbindung weg ist. Dies kannst Du ja einmal testen, indem Du den Code wie folgt anpasst;
- der Client sendet die Daten und schliesst danach die Verbindung. (ggf. noch ein kurzes Thread.Sleep, aber das sollte eigentlich nicht notwendig sein.)
- der Server sendet nichts zurück.

Dann solltest Du erst einmal sehen, ob meine Vermutung korrekt ist.

Wenn dies so ist, dann erweiterst Du die Lösung wie folgt:
Wenn Du das Bytearray hast, dann ermittelst Du erst die Länge. Und ehe Du das byte-Array sendest, sendest Du die Länge. (Also z.B. als zwei Bytes.)
Auf der anderen Seite liest du dann erst die Länge (also die zwei Bytes) und dann liest Du in der Schleife genau diese Anzahl an Bytes.
Sobald Du die Bytes dann da hast, gibst Du das bytearray mit Deinem Code wieder weiter.

Das sollte dann gehen und würde dem Protokoll entsprechen, das ich schon erwähnt habe. Aber du erkennst, dass Du hier dann mehr Daten überträgst als wirklich notwendig. Das Objekt selbst gibt ja schon eine Länge mit somit hast Du jetzt 2 bytes mehr als vorher. Und dann sind da ja auch noch einige Dinge mehr drin wie Klassenname und so.

Du kannst also Dein Byte-Array anders aufbauen (So du mit Byte-Array arbeiten willst). Du kannst für Dein Protokoll z.B. vorsehen, dass Du Nachrichten durchnummerierst:
00: Verbindung beenden. - Hier kommt nichts nach.
01: Nachricht: Zeichenfolge kommt, Ende mit 00
02: Person: Vorname, 00, Nachname, 00, Alter
Dann kannst Du ein Byte lesen und dann hast Du ein Switch Befehl. Bei 0 beendest Du die Verbindung. Bei 1 liest Du Bytes bis Du ein 00 gelesen hast und wandelst das dann als ASCII um in Text. (Also kein Unicode. Wenn Du Unicode haben willst, könntest Du erst eine Byteanzahl bringen oder so.) ....

Dann hast Du ein Protokoll auf Byte-Ebene, welches nicht in so eine Endlosschleife geht.

Aber jetzt habe ich ganz aus den Augen verloren: Das Problem mit der Endlosschleife hast Du verstanden? Dieses while != -1 endet ja erst, wenn die Verbindung beendet wird....

Mit den besten Grüßen,

Konrad
 
Vielen Dank

Ich nehme an, dass du mit deiner Vermutung recht hast.
Allerdings bekomme ich bei dem Versuche bei deserialsieren mit
Java:
 Person p= (Person) ois.readObject();
noch eine
java.i😵ptionalDataException
.

Zu der Endlosschleife:
Ja , ich denk ist jetzt klar. Das war wohl ein Denkfehler von mir. Hab angenommen, wenn -1 zurückgegen wird, wird nichts empfangen.
Aber es wird ist ja wohl so wie z.B bei den BufferdReadern die dann null zurück geben wenn der Output-Steam nicht vorhanden ist und eben nicht dann (wie ich fälschlicherweise dachte)wenn nix gesendet wird.
 

Neue Themen


Zurück
Oben