-1

I have two problems with my java server-client file communication,

I have the CLIENT sends files over to the server and the SERVER receives the files.

My 2 issues are:

1) whenever i send a file, it is 8 bytes less (i do not know why)

2) the file transfer is not complete (with 8 bytes less) unless i close the socket, which i do not want. i want my connection to be persistent, so how can i send a EOF from the client to the server.

here is my client who sends files

public void sendFiles(String file)  {
        try {
            File myFile = new File(file);

            long length = myFile.length();

            byte[] buffer = new byte[8192];
            System.out.println(length);

            FileInputStream in = new FileInputStream(myFile);
            BufferedInputStream bis = new BufferedInputStream(in);
            BufferedOutputStream outF = new BufferedOutputStream(sock.getOutputStream());

            out.print("%SF%" + length + "$" + myFile.getName() + "#");
            out.flush();

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

            outF.flush();
            in.close();
            bis.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}

SERVER who receives files.

I'm passing the name and the length of the file but only using the name of the file. however, i don't know if i need to use the length of the file or not, in case of EOF or something. Please advice

Also, The code hangs in

while ((count = this.sock.getInputStream().read(buffer)) > 0) {

due to no EOF which i do not know how to implement

public void recvFile(String fileName, int length) {

try {
    byte[] buffer = new byte[8192];

    FileOutputStream outF = new FileOutputStream("/Users/Documents" +fileName);
    BufferedOutputStream bos = new BufferedOutputStream(outF);

    int count = length;
    while ((count = this.sock.getInputStream().read(buffer)) > 0) {
        bos.write(buffer, 0, count);
    }
    bos.close();

} catch (IOException ex) {
    ex.printStackTrace();
}
}

UPDATE: I have removed the flush() as advised that it is not needed. Also, i have tested this code in a different class and it worked but it doesn't work here with client-server chat. Could anyone tell me why?

Any help or hints would be appreciated. Thank you.

Deyaa
  • 123
  • 1
  • 2
  • 11
  • Your `recvFile` function should only use the length. You want it to read exactly `length` bytes, right? – user253751 Jan 25 '15 at 02:01
  • Yes, i meant to only read the length of the file then exit the function but it is not working as expected. Plus, it sends the file but incomplete. – Deyaa Jan 25 '15 at 02:03
  • How did you expect it to work? (Your function doesn't actually use the value of the `length` parameter for *anything*, so you can't have expected it to know to read that many bytes) – user253751 Jan 25 '15 at 02:07
  • No, i'm not using it for the moment, because i was trying to for a while with no luck at all because the execution gets stuck in this line " while ((count = this.sock.getInputStream().read(buffer)) > 0) { " and doesn't move until i close the socket which is something else that i dont want. I left length unused for someone to show me how to use it – Deyaa Jan 25 '15 at 02:13
  • TCP streams work similarly to file streams. How would you read an exact number of bytes from a file? – user253751 Jan 25 '15 at 05:53
  • The `count + " bytes left"` is print the number of bytes you just read, not how much is left. BTW if you send more than one message/file you have to check you only read the length you expect. – Peter Lawrey Jan 25 '15 at 09:21
  • You don't need to flush() an output stream before you close it. Buffered a Stream with an 8 KiB buffer when writing 8 KiB at a time is unlikely to be useful. – Peter Lawrey Jan 25 '15 at 09:22
  • @immibis I have not programmed with streams before i'm just learning(you can tell), but from what i have read and saw from the internet and stackoverflow i have similar lines except mine doesn't work. You might see something that i can't see? – Deyaa Jan 25 '15 at 11:34
  • @PeterLawrey i removed the flush(), but i don't think it will solve my issue of not receiving 8 bytes? Plus when i debug the code, the execution gets stuck in this line while ((count = this.sock.getInputStream().read(buffer)) > 0) { and doesn't exit this line until i disconnect the client which is not something i want – Deyaa Jan 25 '15 at 11:37
  • @Deyaa To avoid getting confused as to which stream is writing/reading, I make sure I only call getOutputStream, getInputStream once and only wrap it once as well. Also I try to avoid mixing text and binary as this can be another source of confusion. I suggest using a buffered data steam at a each end and it should work. – Peter Lawrey Jan 25 '15 at 14:14

2 Answers2

1

I would suggest to you send the file size first and/or properties of the file... You can try HTTP which is wide use for this task... Another suggestion would be for you to open another connection on other TCP port just to send the file (this is actually how FTP sends files)

xkothe
  • 633
  • 1
  • 5
  • 18
  • Actually he does send the length and filename (encoded strangely but w/e). – nneonneo Jan 25 '15 at 02:06
  • 1
    @nneonneo: Does he? I see name and length printed to `out` not `outF`. – alk Jan 25 '15 at 10:35
  • @user3558040 How would i use HTTP to achieve that? or how to do to achieve the other suggestion? Thank you – Deyaa Jan 25 '15 at 13:55
  • Hi Deyaa. For HTTP file upload protocol explanation: http://stackoverflow.com/questions/8659808/how-does-http-file-upload-work For FTP I suggest: http://www.ncftp.com/libncftp/doc/ftp_overview.html – xkothe Jan 25 '15 at 15:25
  • @alk: Good catch...but `out` is not defined anywhere so maybe this is even more confusing :P – nneonneo Jan 25 '15 at 21:26
0

I suspect the problem you have is in code you haven't shown.

In this example you can send multiple messages or files over the same stream.

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SocketChannel;

/**
 * Created by peter on 1/25/15.
 */
public class DataSocket implements Closeable {
    private final Socket socket;
    private final DataOutputStream out;
    private final DataInputStream in;

    public DataSocket(Socket socket) throws IOException {
        this.socket = socket;
        this.out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        this.in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
    }

    @Override
    public void close() throws IOException {
        out.flush();
        socket.close();
    }

    // message format is length as UTF-8 encoded name, 32-bit int followed by data.
    public void writeMessage(String description, byte[] bytes) throws IOException {
        out.writeUTF(description);
        out.writeInt(bytes.length);
        out.write(bytes);
        out.flush();
    }

    public byte[] readMessage(String[] description) throws IOException {
        description[0] = in.readUTF();
        int length = in.readInt();
        byte[] bytes = new byte[length];
        in.readFully(bytes);
        return bytes;
    }

    public void writeFile(File file) throws IOException {
        long length = file.length();
        if (length > Integer.MAX_VALUE) throw new IllegalArgumentException("length=" + length);
        out.writeUTF(file.toString());
        out.writeInt((int) length);
        byte[] buffer = new byte[(int) Math.min(length, 32 * 1024)];
        try (FileInputStream fis = new FileInputStream(file)) {
            for (int len; (len = fis.read(buffer)) > 0; ) {
                out.write(buffer, 0, len);
            }
        }
        out.flush();
    }

    public void readFile(File dir) throws IOException {
        String fileName = in.readUTF();
        int length = in.readInt();
        byte[] buffer = new byte[(int) Math.min(length, 32 * 1024)];
        try (FileOutputStream fos = new FileOutputStream(new File(dir, fileName))) {
            while (length > 0) {
                int len = in.read(buffer);
                fos.write(buffer, 0, len);
                length -= len;
            }
        }
    }

    // todo convert to a unit test
    public static void main(String[] args) throws IOException {
        // port 0 opens on a random free port.
        ServerSocket sc = new ServerSocket(0);
        DataSocket ds1 = new DataSocket(new Socket("localhost", sc.getLocalPort()));
        DataSocket ds2 = new DataSocket(sc.accept());
        sc.close();
        // now ds1 and ds2 are connected.
        File f = File.createTempFile("deleteme","");
        f.deleteOnExit();
        try (FileOutputStream fos = new FileOutputStream(f)) {
            fos.write(new byte[10001]);
        }
        // send a request
        ds1.writeMessage("Send me the file", new byte[0]);
        String[] desc = { null };
        byte[] data = ds2.readMessage(desc);
        if (!desc[0].equals("Send me the file")) throw new AssertionError();
    // return a response
        ds2.writeFile(f);
        f.delete();
        if (f.exists()) throw new AssertionError();
        ds1.readFile(new File(""));
        if (f.length() != 10001) throw new AssertionError("length="+f.length());
        ds1.close();
        ds2.close();
        System.out.println("Copied a "+f.length()+" file over TCP");
    }
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Hi Peter, Thank you very much for your answer. As you knew it, yes at the moment i'm using two streams (printWriter and Scanner for texts ) and another streams (in and outF) for sending/receiving files. Should i change all of the streams in both client and server so that they all (sending texts and files) use DataOutputStream and DataInputStream? Then copy what you wrote in writeFile method and use it in my sendFiles method and copy your readFiles method and use it in my recvFile method?. In that way i will unify streams as you mentioned in your answer earlier, then it should work right? – Deyaa Jan 25 '15 at 20:29
  • @Deyaa if you do that it will eliminate a common source of error. – Peter Lawrey Jan 25 '15 at 20:40