# Ich "flip()"pe aus



## Angel4585 (7. Dez 2007)

Moin!

Ich sende Objekte über SocketChannel.

Jetz hab ich das Problem, das wenn ich die Daten am andern Ende empfange, muss ich manchmal ein flip() bei dem ByteBuffer machen, manchmal darf ich es jedoch nicht machen.

Kann mir wer erklären wann ich das tun muss und wann nicht?


----------



## Beni (7. Dez 2007)

Die API meint dazu:


> After a sequence of channel-read or put operations, invoke this method to prepare for a sequence of channel-write or relative get operations. For example: ...


:arrow: immer wenn es zu einem Wechsel zwischen lesendem und schreibendem Zugriff zum Buffer kommt.


----------



## Angel4585 (7. Dez 2007)

kann was passieren wenn ich ein flip() zuviel mache?
Es war nämlich so das manchmal die Daten auf dem ByteBuffer rausgelöscht wurden mit dem flip()


----------



## Angel4585 (7. Dez 2007)

Also was ich mache:


ich erzeuge auf Seite 1 ein Object das ich versenden will.
Dieses packe ich mit einem ObjectOutpuStream in einen ByteArrayOutputStream. Von diesem schicke ich die Größe als Integer mit nem ByteArray das ich mit 4 Byte initialisiert habe nach Seite 2, danach schicke ich den ByteArrayOutputStream rüber.

Auf Seite 2 initialisiere ich ein ByteBuffer mit 4 Byte, lese die Zahl ein, initialisiere ein ByteBuffer mit dieser Zahl und lese dann nochmal.


Jetzt kommt das Phänomen:
Manchmal hat der ByteBuffer in den ich dann eingelesen hab diesen Wert:

java.nio.HeapByteBuffer[pos=151 lim=151 cap=151]

manchmal aber auch diesen:

java.nio.HeapByteBuffer[pos=0 lim=151 cap=151]

wenn ich jetzt bei dem zweiten nochmal ein flip() mache steht da auf einmal:

java.nio.HeapByteBuffer[pos=0 lim=0 cap=151]

aber das flip() mache ich an jeder Stelle wo ich genau so einlese, der Code ist überall identisch...


----------



## Angel4585 (7. Dez 2007)

so.. jetz scheints zu gehn..

nur bekomme ich desöfteren diese Meldung:

java.io.StreamCorruptedException: invalid stream header: 00000000
        at java.ibjectInputStream.readStreamHeader(ObjectInputStream.java:783)
        at java.ibjectInputStream.<init>(ObjectInputStream.java:280)
        at client.com.Controller.readObject(Controller.java:73)
        at client.com.Controller.run(Controller.java:96)


----------



## lhein (7. Dez 2007)

Flip setzt das Limit auf das Ende der Daten im Buffer und die Position wieder auf 0.
Somit ist der Buffer für Leseoperationen vorbereitet.

Bsp.:

```
ByteBuffer bb = ByteBuffer.allocate(size);
            RandomAccessFile raf = new RandomAccessFile(file, "r");
            FileChannel channel = raf.getChannel();
            channel.read(bb);
            channel.close();
            raf.close();
            bb.flip();
```

Nach dem Flip ist der Buffer auf Position 0 und auf Limit "size" gesetzt.

Oder analog die Gegenrichtung:

```
ByteBuffer bb = ByteBuffer.allocate(2);
        bb.clear();
        bb.put(LPR_COMAND_STANDARD.SUB_CMD_ABORT);
        bb.put(SEPARATOR.getBytes());
        bb.flip();
        sendComandToServer(bb);
```

Hier wichtig, nach dem Befüllen des Buffers ein Flip ausführen, damit der Buffer korrekte Limit und Position Informationen hat.

lr


----------



## Angel4585 (7. Dez 2007)

Bei mir siehts etwa so aus:

Versenden mit "write":

```
private void sendObjectInfo(ByteArrayOutputStream baos, Sendable o, SocketChannel aSc)throws IOException{
        ByteBuffer bb = ByteBuffer.allocate(Consts.C_OBJECT_INFO_SIZE);   
        bb.asIntBuffer().put(0,o.getTypeID());
        bb.asIntBuffer().put(1,Integer.valueOf(baos.size()));
        if(bb.position()>0)bb.flip();
        aSc.write(bb);
    }
    
    public void write(Sendable o)throws IOException{   
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        sendObjectInfo(baos,o,sc);
        ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray());
        sc.write(bb);
    }
```

Empfangen mit "read"

```
private ObjectInfo getObjectInfo(SocketChannel aChannel)throws IOException{
        ByteBuffer bb = ByteBuffer.allocate(Consts.C_OBJECT_INFO_SIZE);
        if(aChannel.read(bb)<0)throw new IOException("Server closed Connection");
        if(bb.position()>0)bb.flip();  
        return new ObjectInfo(bb.asIntBuffer().get(0),bb.asIntBuffer().get(1));        
    }
    
    private Object readObject(ObjectInfo aObjectInfo, SocketChannel aChannel)throws IOException, ClassNotFoundException{
        ByteBuffer bb = ByteBuffer.allocate(aObjectInfo.getSize());
        if(aChannel.read(bb)<0)throw new IOException("Server closed Connection");
        if(bb.position()>0)bb.flip();
        ByteArrayInputStream bais = new ByteArrayInputStream(bb.array());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object o = ois.readObject();
        return o;
    }

public void read(SocketChannel asc){
ObjectInfo objInfo = getObjectInfo(asc);
Object obj = readObject(objInfo, asc);
}
```



Das sollte so doch richtig sein oder?
Das problem ist, einmal gehts.. dann wieder nicht.. momentan kommt ständig die Meldung vonwegen "invalid stream header".
Aber die Daten, also Objektinfos usw sind korrekt, also das sollte stimmen.


----------



## lhein (10. Dez 2007)

Überprüfe mal bitte, ob Du auch wirklich beim Lesen die erwartete Menge an Bytes bekommst, sprich der ByteBuffer wirklich voll gefüllt ist. (z.B. in dem Du in der readObject() vor dem Flip() mal die Position ausgibst.

Ansonsten kann ich auf den ersten Blick keinen Fehler erkennen. Die Überprüfung, ob Position > 0, dann flip() kannst Du Dir eigentlich schenken, denn das Flip ist in den Fällen immer richtig. (nämlich nach dem Lesen in den ByteBuffer und nach dem Befüllen eines ByteBuffers über put Methoden) Beim Wrap ist kein Flip erforderlich, aber das hast Du ja richtig gemacht.

Ansonsten würde ich noch überprüfen, ob die Objektversionen auf Client und Serverseite übereinstimmen, sprich die selbe serialVersionUID haben.

lr


----------



## Angel4585 (13. Dez 2007)

ach die brauchen hier auch so eine serialVersionUID? Ich dachte das wär ne Eigenheit von RMI..


----------



## lhein (13. Dez 2007)

Ich denke das ist eine Eigenheit bei jedem Serialisier- / Deserialisiervorgang.

Siehe dazu auch die API: Serializable

lr


----------



## Angel4585 (17. Dez 2007)

Also mit der serialVersionUID hat das nix zu tun. Der Fehler kommt trotzdem.

Ich gehe ja so vor:

Ich schicke die Größe des Objekts in Byte als Integer.

Danach schicke ich das Objekt mit der Größe x.

Beim Empfangen lese ich die Zahl ein, erzeuge einen ByteBuffer mit der Größe x und lese.

Kann es sein, das wenn das Objekt ziemlich groß ist, ich beim Empfänger versuche x Daten zu lesen, obwohl noch garnicht alle x Daten da sind?


----------



## Angel4585 (18. Dez 2007)

weis keiner ne Antwort? Es ist echt seltsam das immerwieder ein Invalid Header auftaucht, also selbst wenn ich von lokal zu lokal verbinde und schicke.


----------

