SocketChannel ByteBuffer Bilder verschicken

Diskutiere SocketChannel ByteBuffer Bilder verschicken im Netzwerkprogrammierung Forum; Hallo an alle! Ich habe ein Problem mit dem Senden von Bildern mithilfe von SocketChannels und ByteBuffern. Da viele Bilder verschickt werden...

  1. BaluEtDespi
    BaluEtDespi Neues Mitglied
    Hallo an alle!
    Ich habe ein Problem mit dem Senden von Bildern mithilfe von SocketChannels und ByteBuffern.
    Da viele Bilder verschickt werden sollen, muss ich auf ein simples Protokoll zurückgreifen, das wie folgt funktionieren soll:
    In ein zuvor gecleartes writeBuffer (ByteBuffer) füge ich die Größe der zu übertragenden Bilddatei ein: ByteBuffer.putLong();
    Dann folgen die Daten des Bildes, die ebenfalls in das Buffer eingefügt werden. Dann wird geflippt und das writeBuffer über den SocketChannel gesendet.
    Auf der Empfängerseite wird immer erst das readBuffer (ByteBuffer) gecleared. Dann wird zunächst die Länge der Bilddatei ausgelesen (ByteBuffer.getLong();). Dann wird das Limit des readBuffers auf DATEILAENGE+8 gesetzt schließlich das readBuffer in einen FileChannel gelesen. Nach diesem Vorgang wird das readBuffer wieder gecleared und auf ein weiteres Bild gewartet.
    Nun das Problem: Bei der ersten Übertragung läuft alles einwandfrei. Doch beim zweiten Eintreffen von Daten im readBuffer ergibt ByteBuffer.getLong() plötzlich völlig absurde Werte, z.B. Server erwartet ein Bild der Groesse: 3927928765588602075.
    Als Quelltext sieht das ganze wie folgt aus:
    Senderseite:
    Code (Java):
    Path path = Paths.get(fileName);
                long fileSize = Files.size(path);
                System.out.println(fileName + " size: " + fileSize);
                socketChannelEntity.getWriteBuffer().putLong(fileSize);
                try{
                    FileChannel fileChannel = FileChannel.open(path);
                    int numRead = 0;
                    int counter = 0;
                   
                    while((numRead = fileChannel.read(socketChannelEntity.getWriteBuffer())) > 0){
                        counter += numRead;
                        socketChannelEntity.getWriteBuffer().flip();
                        do {
                            numRead -= socketChannel.write(socketChannelEntity.getWriteBuffer());
                            socketChannelEntity.getWriteBuffer().clear();
                        } while (numRead > 0);
                    }
                    fileChannel.close();
                    System.out.println("NIO_CLIENT: " + socketChannelEntity.getEntityName() + ": Image " + fileName + " sent: " + counter + " bytes long");
                    socketChannelEntity.setCheckSum(fileSize);
                    socketChannelEntity.setSendReceiveStatus(SendReceiveStatusClient.RECEIVE_CHECKSUM_IMAGE);
                } catch(IOException e) {
                    e.printStackTrace();
                }
    Empfängerseite:
    Code (Java):
    Path path = Paths.get(outputFile);
                FileChannel fileChannel = FileChannel.open(path,
                        EnumSet.of(StandardOpenOption.CREATE,
                                StandardOpenOption.TRUNCATE_EXISTING,
                                StandardOpenOption.WRITE));
                int numRead = 0;
                int counter = 0;
                socketChannel.read(readBuffer);
                readBuffer.flip();
                checkSum = readBuffer.getLong();
                System.out.println("Server erwartet ein Bild der Groesse: " + checkSum);
                readBuffer.limit((int)checkSum+8);
                fileChannel.write(readBuffer);
                fileChannel.close();
                if(readBuffer.hasRemaining()){
                    System.out.println("Ist noch was im ReadBuffer!");
                }
                prepareWriteBuffer(checkSum);
                System.out.println("NIO_SERVER: Received image.");
                sendReceiveStatus = SendReceiveStatusServer.SEND_CHECKSUM_IMAGE;
    Hinweis: das sind jeweils die relevanten Codeausschnitte!
    Ich bin für jede Hilfe dankbar!
    Was mache ich falsch?
     
  2. Vielleicht hilft dir dieses Buch hier weiter.
  3. mihe7
    mihe7 Bekanntes Mitglied
    Du ignorierst, wie viele Daten Dein Buffer tatsächlich zur Verfügung hat, indem Du das Limit auf einen "gewünschten" Wert setzt. Ich würde es mal nach dem folgendem Ansatz probieren.

    Senderseite:
    Code (Java):

    long size = fileChannel.size();
    buf.putLong(size);
    buf.flip();
    do {
        fileChannel.write(buf);
    while (buf.hasRemaining());

    fileChannel.transferTo(0L, size, socketChannel);
     
    Empfängerseite:
    Code (Java):

    boolean expectLength = true;
    long size = 0L;
    while (socketChannel.read(buf) != -1) {
        if (expectLength) {
            if (buf.position() > 7) {
                buf.flip();
                size = buf.getLong();
                expectLength = false;
                buf.compact();
                fileChannel = ...
            }
        } else {
            buf.flip();
            while (size > 0 && buf.hasRemaining()) {
                size -= fileChannel.transferFrom(buf, 0, Math.min(buf.remaining(), size));
            }
            buf.compact();
            if (size == 0L) {
                fileChannel.close();
                expectLength = true;
            }
        }
    }
     
  4. BaluEtDespi
    BaluEtDespi Neues Mitglied
    Danke für Deinen Code. Habe versucht, ihn umzusetzen:
    Exception in thread "Thread-0" java.nio.channels.NonWritableChannelException
    auf Senderseite. Des Weiteren verstehe ich nicht, warum Du nach einfügen der size den Buffer flipst, denn damit überschreibst Du doch diese Daten, wenn die Imagedaten aus dem FileChannel in den Buffer geschrieben werden!?
    Und auf Senderseite schreibe ich doch nicht in den fileChannel, sondern möchte aus diesem LESEN, um die Daten dann in den Buffer zu wrappen.
    Ändere ich die Operation in fileChannel.read(buf) werden die Daten übertragen. Das Problem ist nach wie vor die Empfängerseite, jedoch: FileChannel.transferFrom erwartet als erstes Argument einen ReadableByteChannel. Caste ich per Versuch einfach den ByteBuffer, ist dies natürlich nicht möglich.
    Wie kann es anders funktionieren? Mir fehlen die Ideen.
     
  5. mihe7
    mihe7 Bekanntes Mitglied
    Ach, Mist. Ich hatte das in der Nacht schon geschrieben und heute früh ungelesen reinkopiert :(

    1. Sender: in der do-while-Schleife musst es natürlich socketChannel heißen
    2. Empfänger: da darf es kein transferFrom stehen sondern müsste ein write sein.

    Theoretisch könnte man auch im Anschluss an die while-Schleife mit fileChannel.transferFrom(socketChannel, x, size); den kompletten Rest der Datei einlesen. Aber: wozu? :)
     
  6. BaluEtDespi
    BaluEtDespi Neues Mitglied
    Hey, bin damit einen Schritt weiter gekommen. Habe bei 30 Bildern (nur noch) einen Lost von 6 Bildern, alle anderen werden richtig übertragen. Habe mehrere Versuche repliziert, immer wird ein ganzes Bild geschluckt!? Ich arbeite morgen weiter dran, dann poste ich mal meinen Code.
    Schon mal vielen Dank soweit!
     
  7. BaluEtDespi
    BaluEtDespi Neues Mitglied
    Also hier der Code der Empfängerseite.
    Ergebnis: Die Filesizes werden jeweils richtig gelesen. Als Output werden einige Bilder exakt gespeichert, jedoch existieren zudem noch einige Bilder der Länge 0 ?!?? Was ist falsch? Deine letzte Condition (size == 0) tritt in einigen Fällen gar nicht ein, wenn von size insgesamt mehr Zeichen in den FileChannel geschrieben wurden, da mehr erreichbar waren.

    Code (Java):
    boolean expectLength = true;
                boolean first = true;
                long size = 0L;
                while (socketChannel.read(readBuffer) > 0) {
                    if (expectLength) {
                        if (readBuffer.position() > 7) {
                            readBuffer.flip();
                            size = readBuffer.getLong();
                            if(first){
                                first = false;
                                checkSum = size;
                            }
                            System.out.println("Server expects image of size: " + checkSum);
                            expectLength = false;
                            readBuffer.compact();
                        }
                    } else {
                        readBuffer.flip();
                        while (size > 0 && readBuffer.hasRemaining()) {
                            size -= fileChannel.write(readBuffer);
                        }
                        readBuffer.compact();
                        if (size <= 0L) {
                            fileChannel.close();
                            expectLength = true;
                            System.out.println("Bei fileChannel.close angekommen.");
                        }
                    }
                    readBuffer.clear();
     
  8. mihe7
    mihe7 Bekanntes Mitglied
    Du darfst nicht den ganzen Buffer in die Datei schreiben, sondern nur den Teil, der ggf. noch bis zum Dateieende fehlt. Aus dem Grund erreichst Du manchmal auch nicht den Fall size == 0.
    Code (Java):

               size -= fileChannel.write(buf, 0, Math.min(buf.remaining(), size));
     
     
  9. horstiii2
    horstiii2 Bekanntes Mitglied
    mihe7 , size für gewöhnlich kleiner als buf.remaining() dementsprechend würde ich schreiben: Math.min(size, buf.remaining()). ;)
     
  10. mihe7
    mihe7 Bekanntes Mitglied
    @horstiii2 wenn Du einen 4 KB Buffer verwendest, und 2 MB überträgst, ist size in aller Regel größer als remaining().
     
    horstiii2 gefällt das.
  11. horstiii2
    horstiii2 Bekanntes Mitglied
    Ja ;) Das wäre logisch. Aber 4 KB sind kleiner als 2 MB, damit ist size überwiegend viel kleiner als remaining().
     
  12. Wenn du Java lernen möchtest, empfehlen wir dir dieses Buch hier
Passende Stellenanzeigen aus deiner Region:





Die Seite wird geladen...

SocketChannel ByteBuffer Bilder verschicken - Ähnliche Themen

NIO2 Problem mit AsynchronousSocketChannel beim Schließen
NIO2 Problem mit AsynchronousSocketChannel beim Schließen im Forum Netzwerkprogrammierung
SocketChannel + Write
SocketChannel + Write im Forum Netzwerkprogrammierung
Frage zum Verhalten von SocketChannel.write(.)
Frage zum Verhalten von SocketChannel.write(.) im Forum Netzwerkprogrammierung
FileChannel+SocketChannel:Datei wird nur teilweise übertrage
FileChannel+SocketChannel:Datei wird nur teilweise übertrage im Forum Netzwerkprogrammierung
Was kann SocketChannel was Socket nicht kann?
Was kann SocketChannel was Socket nicht kann? im Forum Netzwerkprogrammierung
Thema: SocketChannel ByteBuffer Bilder verschicken