Basic Java Fileserver

7.5k views Asked by At

I am trying to create a very barebones fileserver that does nothing more than download a zip file and exit, but I am running into problems. I have two main problems.

The first is that when I test on localhost, it sort of works, in that it transfers the zip file, but when I try to open it, I get an error about it being corrupt. It may be something to do with the zip file format, or how I am transferring it.

The second problem is that it fails whenever I use anything but localhost. I have tried a website redirect to my IP, and just putting in my ip address, and both fail, even when I turn off all firewalls and antivirus.

Server code:

import java.io.*;
import java.net.*;

public class FileServer {
    public static void main(String[] args) throws IOException {
    final int PORT_NUMBER = 44444;
    ServerSocket serverSock = null;
    PrintWriter out = null;
    BufferedInputStream bin = null;
    OutputStream os = null;
    Socket clientSock = null;
    File file;
    byte[] fileData;
    String filename = "file.zip";

    while(true) {
        try {
        //Listen on port
        serverSock = new ServerSocket(PORT_NUMBER);

        //Get connection
        clientSock = serverSock.accept();
        System.out.println("Connected client");

        //Get output stream
        out = new PrintWriter(clientSock.getOutputStream(), true);

        out.println(filename);    //Print filename
        file = new File(filename); //Get file
        fileData = new byte[(int)file.length()]; //Stores the file data
        bin = new BufferedInputStream(new FileInputStream(file));
        out.println((int)file.length()); //Print filesize
        bin.read(fileData); //Read contents of file
        os = clientSock.getOutputStream();
        os.write(fileData); //Write the file data
        os.flush();
        } catch(SocketException e) {
        System.out.println("Client disconnected");
        } catch(Exception e) {
        System.out.println(e.getMessage());
        System.exit(1);
        } finally {
        //Close all connections
        System.out.println("Shutting down");

        if(os != null) {
            os.close();
        }

        if(bin != null) {
            bin.close();
        }

        if(out != null) {
            out.close();
        }

        if(clientSock != null) {
            clientSock.close();
        }

        if(serverSock != null) {
            serverSock.close();
        }
        }
    }
    }
}

Client code snippet, assume that all syntax is correct and everything else exists and works, because I probably mismatched some braces or something when I cut the snippet out.

import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;

public static void main(String[] args) {
        final int PORT_NUMBER = 44444;
        final String HOSTNAME = "127.0.0.1";
        String filename = "default.txt";
        Socket sock = null;
        BufferedReader in = null;
        BufferedOutputStream bos = null;
        InputStream is = null;
        byte[] fileData;

        //Attempt to connect
        try {
        sock = new Socket(HOSTNAME, PORT_NUMBER);
        in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        is = sock.getInputStream();
        } catch(UnknownHostException e) {
        JOptionPane.showMessageDialog(this, "Error: could not connect to host " + HOSTNAME + " on port number " + PORT_NUMBER);
        System.exit(1);
        } catch(ConnectException e) {
        JOptionPane.showMessageDialog(this, "Error: connection refused");
        System.exit(1);
        } catch(Exception e) {
        JOptionPane.showMessageDialog(this, e);
        System.exit(1);
        }

        try {
        filename = in.readLine();
        bos = new BufferedOutputStream(new FileOutputStream(filename));
        fileData = new byte[Integer.decode(in.readLine())]; //Gets file size
        is.read(fileData);
        bos.write(fileData);
        bos.flush();
        bos.close();

        if(is != null) {
            is.close();
        }

        if(in != null) {
            in.close();
        }

        if(bos != null) {
            bos.close();
        }

        if(sock != null) {
            sock.close();
        }
        } catch(Exception e) {
        JOptionPane.showMessageDialog(this, e);
        System.exit(1);
        }

        JOptionPane.showMessageDialog(this, "Download complete");
    }
    }
    }                                        

}

EDIT: It works just fine on localhost with .doc and .docx files, it is only .zip that causes problems.

3

There are 3 answers

3
Paŭlo Ebermann On BEST ANSWER

I see two problems on your client, which may cause problems:

You are using a BufferedReader to read the first line, and then access the plain underlying InputStream from the socket with is.read(fileData);

First, the BufferedReader (and it's InputStreamReader) may read more than only the first line on readLine() - the rest is buffered for later retrieval, but you don't read anything more. So you may be missing the start of your file.

Second, you have only one main read for your socket-InputStream, which may or may not fill the whole array, since you don't check the result of this method call. Thus the end of the file may be consisting of zero-bytes instead of the real data (and certainly there will be as much zero bytes as you missed for the first problem). You should instead read (and then maybe instantly write to the file) in a loop, checking (and adding) the results of the read call.

Edit: For reading only into the array:

int read = 0;
while(read < size) {
    int r = is.read(fileData, read, size-read);
    if(r < 0) {
       // end of file, should not occur if noone interrupts your stream or such
       throw new EOFException("input ended prematurely");
    }
    read += r;
}

There is a readFully() method in DataInputStream which does just this, I think. If you have bigger files, you would not want it completely in one array, but read and write alternately (using a smaller array as a buffer):

InputStream is = ...;
int len = ...;
OutputStream out = ...;

int read = 0;
byte[] buf = new byte[8000];
while(read < len) {
    int r = is.read(buf);
    if(r < 0) {
       // end of file, should not occur if noone interrupts your stream or such
       throw new EOFException("input ended prematurely");
    }
    out.write(buf, 0, r);
    read += r;
}

I can only speculate why it works with doc - did you really compare the output files? Maybe your Word documents are smaller then your zip file, and thus the second problem did not really apply, since all the file could be read in one step.

The same (second) problem actually applies to your server for reading the file, too.

You may think about using a DataInputStream (and DataOutputStream at the server side) - it allows reading Strings too, without needing a Writer (and additional Buffering).

2
Aif On

Did you try it on your internal IP? It should also work, and the problem would only be NAT or stuff like that from the internet.

For your file, are the md5sums identical?

1
ajitatif On

You should set the IP you're listening to your LAN IP. You're using 127.0.0.1; which accepts requests from localhost only.