0

I was experimenting with JAVA and found this question online.

Java sending and receiving file (byte[]) over sockets.

Just for curiosity i played with the code in the accepted answer, and with other code i found similar to the question. I tried the accepted answer,yes it works and is very fast. But the problem is Archive files are getting corrupted. So here is other code i tried. The downfall of my experimental code is it consume CPU cycles and takes more time than accepted answer (And i have no idea why it is happening so). So here is my code. Can somebody help me to optimize and improve this code more.

Time Taken by accepted Answer = 11ms for 4 Mb file. Time taken by my experiment= 4 seconds for same file.

Server.java

public class Server implements Runnable {

 private ServerSocket serverSocket = null;
 private Socket socket = null;
 private ObjectInputStream inStream = null;

 public Server() {

 }

 @Override
 public void run() {

     try {
    serverSocket = new ServerSocket(4445);
    socket = serverSocket.accept();
    DataInputStream dIn = new DataInputStream(socket.getInputStream());
    OutputStream os = socket.getOutputStream();
    DataOutputStream outToClient = new DataOutputStream(os);

    System.out.println("Connected");
    File myFile = new File("lib1.zip");
    long flength = myFile.length();
    System.out.println("File Length"+flength);
    outToClient.writeLong(flength);
    FileInputStream fis;
    BufferedInputStream bis;
    byte[] mybytearray = new byte[8192];
    fis = new FileInputStream(myFile);
    bis = new BufferedInputStream(fis);
    int theByte = 0;
    System.out.println("Sending " + myFile.getAbsolutePath() + "(" + myFile.length() + " bytes)");
       while ((theByte = bis.read()) != -1) {
     outToClient.write(theByte);
     // bos.flush();
     }
    /*int count;
    BufferedOutputStream bos= new BufferedOutputStream(os);
    while ((count = bis.read(mybytearray))>0) {
        bos.write(mybytearray, 0, count);
    }*/

    bis.close();
    socket.close();

} catch (SocketException se) {

    System.exit(0);
} catch (IOException e) {
    e.printStackTrace();
}
 }

 public static void main(String[] args) {
     Thread t = new Thread(new Server());
     t.start();
 }
 }

ReceiveFile.java

 public class RecieveFile {

 public final static int SOCKET_PORT = 4445;      // you may change this
 String SERVER = "127.0.0.1";  // localhost
 ArrayList<String> logmsg = new ArrayList<>();
 public static void main(String[] args) {
     new RecieveFile();
 }

 public RecieveFile() {
     try (Socket sock = new Socket(SERVER, SOCKET_PORT)) {

    System.out.println("Connecting...");
    try (OutputStream os = sock.getOutputStream(); DataOutputStream outToServer = new DataOutputStream(os)) {
        try (DataInputStream dIn = new DataInputStream(sock.getInputStream())) {
            long fileLen, downData;
            int bufferSize = sock.getReceiveBufferSize();

            long starttime = System.currentTimeMillis();
            File myFIle = new File("lib1.zip");
            try (FileOutputStream fos = new FileOutputStream(myFIle); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                fileLen = dIn.readLong();
                /*for (long j = 0; j <= fileLen; j++) {
                 int tempint = is.read();
                 bos.write(tempint);
                 }*/
                downData = fileLen;
                int n = 0;
                byte[] buf = new byte[8192];
                    while (fileLen > 0 && ((n = dIn.read(buf, 0, buf.length)) != -1)) {
                 bos.write(buf, 0, n);
                 fileLen -= n;
                 //            System.out.println("Remaining "+fileLen);
                 }
                /*while ((n = dIn.read(buf)) > 0) {
                    bos.write(buf, 0, n);
                }*/
                bos.flush();
                long endtime = System.currentTimeMillis();
                System.out.println("File " + myFIle.getAbsolutePath()
                        + " downloaded (" + downData + " bytes read) in " + (endtime - starttime) + " ms");

            }
        }
    }
} catch (IOException ex) {
    Logger.getLogger(RecieveFile.class.getName()).log(Level.SEVERE, null, ex);
}

 }
 }
DeepSidhu1313
  • 805
  • 15
  • 31

3 Answers3

1

Your solution takes a lot of time probably because you are reading a character at time, instead of all the buffer.

The solution is to use a construct similar to the linked question; the problem you got about corrupted file is really improbable, a malformed TCP packed that pass CRC check is really rare occurrence, and I would blame a bug instead. try to post the code you used. But you can add some hash check on the file and some part of it, if you are concerned about this

Lesto
  • 2,260
  • 2
  • 19
  • 26
1

You're copying a byte at a time. This is slow. You're also declaring a byte array but not using it. Try this:

int count;
byte[] buffer = new byte[8192]; // or more, double or quadruple it

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}
user207421
  • 305,947
  • 44
  • 307
  • 483
  • `> 0` is erroneous. It's possible that sometimes no bytes are sent. In your case, it'd mean that the file would be incompletely downloaded. – Olivier Grégoire Jun 27 '15 at 09:47
  • @OlivierGrégoire Rubbish. The only circumstance under which `InputStream.read(byte[])` can return zero is if you provided a zero-length buffer. Streams work in blocking mode, which by definition blocks until at least one byte is available or end of stream or an error occurs. It cannot possibly return zero just because no data arrived. See the Javadoc. – user207421 Jun 27 '15 at 10:25
  • @EJP Thanks, but i didn't get it, you marked my question duplicate, and there is some guy asking me to post a new question, sometimes you guys are so confusing. LOL, anyways thanks :) – DeepSidhu1313 Jun 27 '15 at 16:30
  • @DeepSidhu1313 It *is* a duplicate. Your posting there was a new but pointless question because the answer is the same. – user207421 Jun 27 '15 at 18:45
  • @EJP i am getting error while transferring small files of 899 bytes. EOF exception when reading at client side. Am i still missing some bytes? – DeepSidhu1313 Jul 14 '15 at 11:13
  • @EJP also sometimes SHA1 doesn't match for files on both sides. Happens after running file server for 5-6 files. – DeepSidhu1313 Jul 14 '15 at 11:16
  • @EJP Thanks looks like flushing bos on server side solved the problem. What happens if we use -1 instead 0 as `while ((count = in.read(buffer)) > -1) { out.write(buffer, 0, count); }` ?? – DeepSidhu1313 Jul 14 '15 at 11:26
0

Here is a cleaned up version of your code, it should perform faster as it avoids single byte operations:

public class Server implements Runnable {
    @Override
    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(4445);
            Socket socket = serverSocket.accept();
            OutputStream os = socket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);

            File myFile = new File("lib1.zip");
            long flength = myFile.length();
            dos.writeLong(flength);
            InputStream fis = new FileInputStream(myFile);

            byte[] buf = new byte[16*1024]; // 16K
            long written = 0;

            while ((count = fis.read(buf))>0) {
                dos.write(buf, 0, count);
                written+=count;
            }

            if (written != flength)
                System.out.println("Warning: file changed");
            dos.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit();
        }
 }

An possible improvement would be to use NIO with channel.sendTo() but this should already have an acceptable performance. Note you do not need to use buffered streams on reading or writing as you use a larger byte array buffer anyway.

One possible improvement would be to not use the DataOutputStream for the long but poke the 8 bytes of it into the first buffer (array) write.

BTW: writing 4MB in 11ms is 390MB/s, that would be faster than most desktop disks can read and write.

eckes
  • 10,103
  • 1
  • 59
  • 71
  • `> 0` is erroneous. It's possible that sometimes no bytes are sent. In your case, it'd mean that the file would be incompletely downloaded. – Olivier Grégoire Jun 27 '15 at 09:49
  • @eckes `Connecting... File ~/NetBeansProjects/lib1/lib1.zip downloaded (4452644 bytes read) in 11 ms BUILD SUCCESSFUL (total time: 0 seconds)` – DeepSidhu1313 Jun 27 '15 at 16:33
  • 1
    @OlivierGrégoire I had the same understanding but it is wrong, read is guranteed to return at least 1 byte or block. – eckes Jun 27 '15 at 17:39
  • Except when it doesn't, like I had a few years back in a bug that took 3 days to find. Possibly a bug in the JVM, but it happened. – Olivier Grégoire Jun 28 '15 at 14:17
  • @OlivierGrégoire I occasionally hear vague and unsubstantiated reports like this, but I've been relying on it never returning zero for over 18 years, and I've never been disappointed. No doubt your problem was elsewhere: a zero length buffer, or zero count. – user207421 Jun 29 '15 at 11:22
  • @EJP Yeah, whatever, when I use `!= 1` instead of `> 0`, I never had any issue. – Olivier Grégoire Jun 29 '15 at 11:50