72

I am trying to develop a very simple client / server where the client converts a file to bytes, sends it to the server, and then converts the bytes back in to a file.

Currently the program just creates an empty file. I'm not a fantastic Java developer so any help much appreciated.

This is the server part that receives what the client sends.

ServerSocket serverSocket = null;

    serverSocket = new ServerSocket(4444);


    Socket socket = null;
    socket = serverSocket.accept();

    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
    byte[] bytes = new byte[1024];

    in.read(bytes);
    System.out.println(bytes);

    FileOutputStream fos = new FileOutputStream("C:\\test2.xml");
    fos.write(bytes);

And here is the client part

Socket socket = null;
    DataOutputStream out = null;
    DataInputStream in = null;
    String host = "127.0.0.1";     

    socket = new Socket(host, 4444);
    out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

    File file = new File("C:\\test.xml");
    //InputStream is = new FileInputStream(file);
    // Get the size of the file
    long length = file.length();
    if (length > Integer.MAX_VALUE) {
        System.out.println("File is too large.");
    }
    byte[] bytes = new byte[(int) length];

    //out.write(bytes);
    System.out.println(bytes);

    out.close();
    in.close();
    socket.close();
Rookie
  • 1,879
  • 3
  • 17
  • 18

6 Answers6

87

The correct way to copy a stream in Java is as follows:

int count;
byte[] buffer = new byte[8192]; // or 4096, or more
while ((count = in.read(buffer)) > 0)
{
  out.write(buffer, 0, count);
}

Wish I had a dollar for every time I've posted that in a forum.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thank you! I've looked at the read method - where you've written count, that'll be the length of the file is that correct? Also, how would you reverse that code when the bytes are received? – Rookie Mar 03 '12 at 11:25
  • 1
    No, `count` is an `int` variable where the result of each `read()` method call is stored, as the code says. The code when receiving is identical, just different ins and outs. – user207421 Mar 03 '12 at 11:28
  • Alternate correct way: Guava's `ByteSTreams.copy(InputStream from, OutputStream to)` – yshavit Dec 30 '15 at 21:53
  • @yshavit There are dozens of wrappers for it, but they all execute this code, and if they don't they should. – user207421 Jun 03 '16 at 02:32
  • 1
    how do I set the buffer size on the server when the size of the received file is previously unknown? – keinabel Nov 27 '16 at 14:41
  • @keinabel It doesn't matter what buffer size you use. This code works for any buffer size greater than zero, and it doesn't need to be the same at both ends. – user207421 Nov 27 '16 at 17:18
  • @EJP `InputStream.read()` can return 0 length and still remain open for further reading, so I think the condition should state `count != -1`. – diginoise Nov 08 '17 at 16:42
  • @diginoise It can only return zero if you provide a zero length buffer, which is a programming error you don't want to loop forever on. – user207421 Nov 19 '17 at 21:15
  • @user207421 can you tell me more about the buffer's size? I really don't understand, which is the better size, or when use 8192 than 4096, what affect if I put a big size like 32768? Thanks in advance.. – Ruben Flores Dec 16 '19 at 13:51
  • 1
    @RubenFlores The bigger the better, of course, but there isn't much bang for the buck beyond a few K, as the network can only transmit about 1500 bytes at a time. – user207421 Apr 26 '20 at 21:17
81

Thanks for the help. I've managed to get it working now so thought I would post so that the others can use to help them.

Server:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(4444);
        } catch (IOException ex) {
            System.out.println("Can't setup server on this port number. ");
        }

        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        
        try {
            socket = serverSocket.accept();
        } catch (IOException ex) {
            System.out.println("Can't accept client connection. ");
        }
        
        try {
            in = socket.getInputStream();
        } catch (IOException ex) {
            System.out.println("Can't get socket input stream. ");
        }

        try {
            out = new FileOutputStream("M:\\test2.xml");
        } catch (FileNotFoundException ex) {
            System.out.println("File not found. ");
        }

        byte[] bytes = new byte[16*1024];

        int count;
        while ((count = in.read(bytes)) > 0) {
            out.write(bytes, 0, count);
        }

        out.close();
        in.close();
        socket.close();
        serverSocket.close();
    }
}

and the Client:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = null;
        String host = "127.0.0.1";

        socket = new Socket(host, 4444);
        
        File file = new File("M:\\test.xml");
        // Get the size of the file
        long length = file.length();
        byte[] bytes = new byte[16 * 1024];
        InputStream in = new FileInputStream(file);
        OutputStream out = socket.getOutputStream();
        
        int count;
        while ((count = in.read(bytes)) > 0) {
            out.write(bytes, 0, count);
        }

        out.close();
        in.close();
        socket.close();
    }
}
user207421
  • 305,947
  • 44
  • 307
  • 483
Rookie
  • 1,879
  • 3
  • 17
  • 18
  • 8
    There is no reason to waste space by allocating a buffer the size of the entire file: this doesn't scale to large files, or work at all for files over 2GB. A buffer of 8192 is adequate for most purposes. It doesn't have to have anything to do with the socket receive buffer size either. You don't need any of the flush() calls and you only need to close 'out' and 'bis'. Too much unnecessary and wasteful code here. – user207421 Jul 04 '14 at 14:55
  • 3
    `if (length > Integer.MAX_VALUE) ` should be `if (length > Long.MAX_VALUE)` because `length` is `long` not `int` – Altiano Gerung Jun 07 '15 at 09:03
  • @EJP Zip Files Sent using above method are unable to read on receiver's side. Files are getting corrupted. – DeepSidhu1313 Jun 21 '15 at 16:11
  • @DeepSidhu1313 Why are you telling me? I didn't post this code. But I don't see anything here that will corrupt the data, as long as you copy it correctly. Why you would do that when you can use the four lines of code I provided in my own answer is another question. – user207421 Jun 27 '15 at 09:13
  • 1
    @AltianoGerung the reason this check is done is because a array cannot be larger. But the check is not needed if the array has a much smaller fixed size (16k). I removed this part, as for an accepted answer it should not be that platantly wrong. – eckes Jun 27 '15 at 09:20
  • The exception handling here is also entirely incorrect. Code that depends on the success of code in a prior try block should be inside the same try block. This code will not survive most exceptions correctly. – user207421 Sep 22 '15 at 12:00
  • 4
    @AltianoGerung A `long` *can't* be greater than `Long.MAX_VALUE`. Your suggestion does not make sense. – user207421 May 16 '16 at 20:06
  • 2
    I'm using the byte method for sending a image file from a client to a server. Does anyone else get stuck up on the while loop. I get it to send the file and I can open it while the server is running but the file says it is 0 byte in size. Any Ideas? – Kayracer Sep 09 '17 at 02:59
  • @Kayracer See my answer for the easy, correct way. – user207421 Nov 19 '17 at 21:30
5

Here is the server Open a stream to the file and send it overnetwork

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleFileServer {

  public final static int SOCKET_PORT = 5501;
  public final static String FILE_TO_SEND = "file.txt";

  public static void main (String [] args ) throws IOException {
    FileInputStream fis = null;
    BufferedInputStream bis = null;
    OutputStream os = null;
    ServerSocket servsock = null;
    Socket sock = null;
    try {
      servsock = new ServerSocket(SOCKET_PORT);
      while (true) {
        System.out.println("Waiting...");
        try {
          sock = servsock.accept();
          System.out.println("Accepted connection : " + sock);
          // send file
          File myFile = new File (FILE_TO_SEND);
          byte [] mybytearray  = new byte [(int)myFile.length()];
          fis = new FileInputStream(myFile);
          bis = new BufferedInputStream(fis);
          bis.read(mybytearray,0,mybytearray.length);
          os = sock.getOutputStream();
          System.out.println("Sending " + FILE_TO_SEND + "(" + mybytearray.length + " bytes)");
          os.write(mybytearray,0,mybytearray.length);
          os.flush();
          System.out.println("Done.");
        } catch (IOException ex) {
          System.out.println(ex.getMessage()+": An Inbound Connection Was Not Resolved");
        }
        }finally {
          if (bis != null) bis.close();
          if (os != null) os.close();
          if (sock!=null) sock.close();
        }
      }
    }
    finally {
      if (servsock != null)
        servsock.close();
    }
  }
}

Here is the client Recive the file being sent overnetwork

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class SimpleFileClient {

  public final static int SOCKET_PORT = 5501;
  public final static String SERVER = "127.0.0.1";
  public final static String
       FILE_TO_RECEIVED = "file-rec.txt";

  public final static int FILE_SIZE = Integer.MAX_VALUE;

  public static void main (String [] args ) throws IOException {
    int bytesRead;
    int current = 0;
    FileOutputStream fos = null;
    BufferedOutputStream bos = null;
    Socket sock = null;
    try {
      sock = new Socket(SERVER, SOCKET_PORT);
      System.out.println("Connecting...");

      // receive file
      byte [] mybytearray  = new byte [FILE_SIZE];
      InputStream is = sock.getInputStream();
      fos = new FileOutputStream(FILE_TO_RECEIVED);
      bos = new BufferedOutputStream(fos);
      bytesRead = is.read(mybytearray,0,mybytearray.length);
      current = bytesRead;

      do {
         bytesRead =
            is.read(mybytearray, current, (mybytearray.length-current));
         if(bytesRead >= 0) current += bytesRead;
      } while(bytesRead > -1);

      bos.write(mybytearray, 0 , current);
      bos.flush();
      System.out.println("File " + FILE_TO_RECEIVED
          + " downloaded (" + current + " bytes read)");
    }
    finally {
      if (fos != null) fos.close();
      if (bos != null) bos.close();
      if (sock != null) sock.close();
    }
  }    
}
Jay Godara
  • 178
  • 4
  • 11
  • 1
    See my comment on [the other answer that says the same thing](http://stackoverflow.com/a/9548429/207421). – user207421 May 22 '15 at 06:37
0

To avoid the limitation of the file size , which can cause the Exception java.lang.OutOfMemoryError to be thrown when creating an array of the file size byte[] bytes = new byte[(int) length];, instead we could do

    byte[] bytearray = new byte[1024*16];
    FileInputStream fis = null;
    try {

        fis = new FileInputStream(file);
        OutputStream output= socket.getOututStream();
        BufferedInputStream bis = new BufferedInputStream(fis);

        int readLength = -1;
        while ((readLength = bis.read(bytearray)) > 0) {
            output.write(bytearray, 0, readLength);

        }
        bis.close();
        output.close();
    }
    catch(Exception ex ){

        ex.printStackTrace();
    } //Excuse the poor exception handling...
QuakeCore
  • 1,886
  • 2
  • 15
  • 33
-1

Rookie, if you want to write a file to server by socket, how about using fileoutputstream instead of dataoutputstream? dataoutputstream is more fit for protocol-level read-write. it is not very reasonable for your code in bytes reading and writing. loop to read and write is necessary in java io. and also, you use a buffer way. flush is necessary. here is a code sample: http://www.rgagnon.com/javadetails/java-0542.html

horaceman
  • 375
  • 1
  • 5
  • Ah this is useful thank you! Question - the receive code, the filesize to be received his set statically. How would you go about setting this dynamically? Is there a way of sending the file length before the bytes array maybe? – Rookie Mar 03 '12 at 11:32
  • 2
    You can't use FileOutputStream to a socket, and there's nothing wrong with using DataOutputStream in this way. This answer doesn't make sense. – user207421 Jan 15 '14 at 20:17
  • 1
    And `flush()` is not necessary before `close(),` and the code in the link you provided doesn't do any of the things you've recommended here. It also doesn't work. – user207421 May 22 '15 at 06:40
  • 1
    @LucasAmos It is garbage. For example, the author doesn't explain how the client is magically going to know the fie size in advance, or why the entire file should be loaded into memory at both ends. It will fail spectacularly on empty files. File copying is far simplet than this. – user207421 Nov 06 '17 at 17:21
-2

Adding up on EJP's answer; use this for more fluidity. Make sure you don't put his code inside a bigger try catch with more code between the .read and the catch block, it may return an exception and jump all the way to the outer catch block, safest bet is to place EJPS's while loop inside a try catch, and then continue the code after it, like:

int count;
byte[] bytes = new byte[4096];
try {
    while ((count = is.read(bytes)) > 0) {
        System.out.println(count);
        bos.write(bytes, 0, count);
    }
} catch ( Exception e )
{
    //It will land here....
}
// Then continue from here

EDIT: ^This happened to me cuz I didn't realize you need to put socket.shutDownOutput() if it's a client-to-server stream!

Hope this post solves any of your issues

Camil Latif
  • 33
  • 1
  • 5
  • You don't need `shutdownOutput()`. Closing the socket has the same effect. If you get an `IOException` while copying, you do want to stop immediately. There is almost certainly nothing more you can do withhe socket except close it. – user207421 May 18 '18 at 08:17