0

This is related to my previous question - DataInputStream giving java.io.EOFException

In that Client-Server app there is method to retrieve file sent from server and save to a file.

Client.java -

 public void receiveFile(InputStream is, String fileName) throws Exception {
        int filesize = 6022386;
        int bytesRead;
        int current = 0;
        byte[] mybytearray = new byte[filesize];

        System.out.println("Receving File!");
        FileOutputStream fos = new FileOutputStream("RECEIVED_"+fileName);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bytesRead = is.read(mybytearray, 0, mybytearray.length);
        current = bytesRead; 
        System.out.println(bytesRead);
        do {
            bytesRead = is.read(mybytearray, current,
                    (mybytearray.length - current));
            System.out.println(bytesRead);
            if (bytesRead >= 0)
                current += bytesRead;
        } while (bytesRead > -1);
        System.out.println("Loop done");
        bos.write(mybytearray, 0, current);
        bos.flush();
        bos.close();
    }
}

Server.Java

public void sendFile(OutputStream os, String fileName) throws Exception {
    File myFile = new File(fileName);
    byte[] mybytearray = new byte[(int) myFile.length() + 1];
    FileInputStream fis = new FileInputStream(myFile);
    BufferedInputStream bis = new BufferedInputStream(fis);
    bis.read(mybytearray, 0, mybytearray.length);
    System.out.println("Sending File!");
    os.write(mybytearray, 0, mybytearray.length);
    os.flush();
    bis.close();
}

As you can see there are several stranded outs in client's receiveFile method. here is the output i received.

enter image description here

The issue is that method don't complete its task and never reached to System.out.println("Loop done");

What's the issue ?

Community
  • 1
  • 1
ChamingaD
  • 2,908
  • 8
  • 35
  • 58
  • Note that System.out is, by default, buffered. It's not a good idea to use it as an indication of how far your code has gone. Instead, use System.err or a logger API – NamshubWriter Apr 20 '14 at 15:44

3 Answers3

1

I'll come to the loop error later.

There is one little byte too much at the source:

public void sendFile(OutputStream os, String fileName) throws Exception {
    File myFile = new File(fileName);
    if (myFile.length() > Integer.MAX_VALUE) {
        throw new IllegalStateException();
    }

Either

    byte[] mybytearray = Files.readAllBytes(myFile.toPath());

Or

    byte[] mybytearray = new byte[(int) myFile.length()]; // No +1.
    // BufferedInputStream here not needed.
    try (BufferedInputStream bis = new BufferedInputStream(
            new FileInputStream(myFile))) {
        bis.read(mybytearray);
    } // Always closed.

and then

    System.out.println("Sending File!");
    os.write(mybytearray);
    os.flush();
}

Alternatively I have added the java 7 Files.readAllBytes. It could even be simpler using Files.copy.

I just see the main error is mentioned already. Basically there is a misconception: you may read a byte array in its entirety. It will block till the end is read. If there is less to read ("end-of-file" reached), then the number of bytes is returned. So to all purposes you might read it in a whole.

One often sees similar code to read a fixed size (power of 2) block (say 4096) repeated and written to the output stream.

Again java 7 Files simplifies all:

    Files.copy(is, Paths.get("RECEIVED_" + fileName));

In Short:

public void receiveFile(InputStream is, String fileName) throws Exception {
    Files.copy(is, Paths.get("RECEIVED_" + fileName),
        StandardCopyOption.REPLACE_EXISTING);
}

public void sendFile(OutputStream os, String fileName) throws Exception {
    Files.copy(Paths.get(fileName), os);
}
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Added the code to the answer; there are several copy options you might like to add. Above is REPLACE_EXISTING. – Joop Eggen Apr 20 '14 at 18:37
  • Changed the methods as you mentioned. But its still same. Here is the complete code. Can you have a look ? https://dl.dropboxusercontent.com/u/4959382/Documents/server.txt https://dl.dropboxusercontent.com/u/4959382/Documents/client.txt – ChamingaD Apr 21 '14 at 16:30
  • See https://drive.google.com/file/d/0B_vx2TgdG0SMdWNOWV83ckpkMHc/edit?usp=sharing but don't overwrite your code with it. Object- or DataInputStream is too specific, so I started a mixed text (for commands) + binary data (for files) approach. But the client part must fit too. `put`and `get` of a file should here be the last command, without a `bye` or so. Also I could not refrain from using threading as intended. Normally with a limited thread pool (ThreadPoolExecutor I believe). – Joop Eggen Apr 21 '14 at 20:24
  • Wow! Thanks a lot. Did you made changes to client as well ? I checked my old client with your sever. But didn't work properly. – ChamingaD Apr 22 '14 at 15:36
  • No client done. I did not go to the extend of testing the code, so I thought you might as well work out the correct functioning. Mind, the code uses no longer java object streaming. – Joop Eggen Apr 22 '14 at 17:13
  • Can i get the client as well ? You have uploaded only sever. Is there any reason not to use ObjectInputStream ? The assignment says us to use it. – ChamingaD Apr 23 '14 at 02:09
  • If it is an assignment, do use ObjectInputStream. It is a pure binary, java specific, protocol; and we are partly dealing with text (which is easier to develop with). Also use `byte[]` then, as you did, and read the array in its entirety with one call to `read(byte[])`. The rest is moot. – Joop Eggen Apr 23 '14 at 06:45
  • @ChamingaD in the future, if you are asking something that is homework, state that when asking the question. See http://meta.stackexchange.com/questions/10811/how-do-i-ask-and-answer-homework-questions – NamshubWriter Apr 24 '14 at 15:58
0

You probably want your condition to read:

} while (bytesRead > -1 && current < mybytearray.length);
//                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

At the moment, when you have read the entire file, your read statement keeps reading zero bytes (mybytearray.length - current) which succeeds and returns zero, so your loop never terminates.

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
0

It would be much simpler to not keep appending to the input buffer

public void receiveFile(InputStream istream, String fileName) throws Exception {
  System.err.println("Receving File!");
  try (BufferedOutputStream ostream = new BufferedOutputStream(
      new FileOutputStream("RECEIVED_" + fileName))) {
    int bytesRead;
    byte[] mybytearray = new byte[1024];

    do { 
      bytesRead = istream.read(mybytearray);
      if (bytesRead > 0) {
        ostream.write(mybytearray, 0, bytesRead);
      }
    } while (bytesRead != -1);
    System.err.println("Loop done");
    ostream.flush();
  }
}

If you can use Guava, you can use ByteStreams.copy(java.io.InputStream, java.io.OutputStream) (Javadoc)

NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
  • Thanks. But the code you given has some syntax errors. Can you fix it ? – ChamingaD Apr 20 '14 at 16:01
  • @ChamingaD I only saw one syntax error. Note the code uses the try-with-resources statement, which was added in Java SE 7. In any case, I really recommend using Guava. It has a lot of really useful classes for dealing with I/O, collections and functions – NamshubWriter Apr 20 '14 at 16:05
  • ...or if you use JDK7, just go with Joop's suggestion and use Files.copy() in both of your methods – NamshubWriter Apr 20 '14 at 16:14
  • http://i.imgur.com/pAvWJH1.png Syntax error on token "do", delete this token Cannot refer to a non-final variable is inside an inner class defined in a different method – ChamingaD Apr 20 '14 at 16:44
  • @ChamingaD fixed. Sorry about that; I didn't have a compiler handy when I wrote that. As I said before, if you are using JDK7, Joop's answer is better. If you are using JDK6, you would have to remove the usage of try-with-resources in my answer. – NamshubWriter Apr 21 '14 at 16:04