2
\$\begingroup\$

I am making a program that transfers files from one client to another. Right now I am just working on sending the file to the server and then later I will work on transferring it to the other client. Here is my client class*:

public class Client implements Runnable {

    private Socket socket;
    private InputStream input;
    private OutputStream output;
    private PrintWriter printer;
    private BufferedReader reader;

    private boolean running = true;
    private long ping = System.nanoTime() / 1000000;

    public Client(String address) {
        String host = address.substring(0, address.indexOf(":"));
        int port = Integer.parseInt(address.substring(address.indexOf(":") + 1));
        System.out.println("Attempting to connect to " + host + ":" + port);
        try {
            socket = new Socket(host, port);
            printer = new PrintWriter(socket.getOutputStream(), true);
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            output = socket.getOutputStream();
            System.out.println("Successfully connected!");
            running = true;
        } catch (IOException e) {
            e.printStackTrace();
            running = false;
            System.out.println("Failed to connect.");
            Thread.currentThread().interrupt();
            ;
        }
    }

    @Override
    public void run() {
        if (!running)
            return;
        String str;
        printer.println("ping");
        try {
            while (running && (str = reader.readLine()) != null) {
                handle(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.close();
    }

    public void handle(String str) {
        if (str.equals("ping")) {
            send("pong");

        } else if (str.equals("pong")) {
            ping = ((System.nanoTime() / 1000000) - ping) / 2;
            System.out.println("Ping: " + ping + "ms");
            File file = new File("./res/" + "send.zip");
            send("pong");

            for (int lcv = 0; lcv < 1; lcv++) {
                sendFile(file);
                wait(1000);
            }

        }
        if (str.equals(SocketKey.CLOSE_CONNECTION)) {
            running = false;
            printer.println(SocketKey.CLOSE_CONNECTION);
        }
    }

    public void sendFile(File file) {
        long startTime = (System.nanoTime() / 1000000);
        long fileSize = file.length();
        send("new_file:[" + file.getName() + "][" + fileSize + "]");
        System.out.println("Name: " + file.getName() + " Size: " + Util.readableFileSize(fileSize));
        System.out.print("Transferring file");
        byte[] bytes = new byte[16 * 1024];

        try {
            input = new FileInputStream(file);
            int count, totalCount = 0;
            double perc = 0;
            while ((count = input.read(bytes)) > 0) {
                totalCount += count;
                if (perc < ((double) totalCount) / fileSize) {
                    System.out.print(".");
                    perc += .1;
                }
                do {
                    output.write(bytes, 0, count);

                } while (waitFor(".", 1000) == null);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String str;
        try {
            while (running && (str = reader.readLine()) != null) {
                if (str.equals("Complete.") || str.equals("Failed.")) {
                    System.out.println(
                            "\nFile successfully sent! (" + ((System.nanoTime() / 1000000) - startTime) + "ms)");
                    break;
                } else
                    handle(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String[] waitFor(String string, long ms) {
        String str;
        List<String> strings = new ArrayList<String>();
        long startTime = (System.nanoTime() / 1000000);
        try {
            while ((str = reader.readLine()) != null) {
                if (str.equals(string) || !running)
                    break;
                else if ((System.nanoTime() / 1000000) - startTime < ms)
                    return null;
                else
                    strings.add(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String[] ret = new String[strings.size()];
        for (int lcv = 0; lcv < strings.size(); lcv++)
            ret[lcv] = strings.get(lcv);
        return ret;
    }

    public void close() {
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void send(Object... objects) {
        String output = "";
        for (Object obj : objects) {
            output += obj.toString();
        }
        printer.println(output);
    }

    public void wait(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws UnknownHostException, IOException {
//      startUI();
        Client client = new Client("localhost:1123");
        new Thread(client).start();
//      File file = new File("./res/" + "");
//      client.sendFile(file);

    }
}

here is my server class*:

public class Server {

    public static boolean running = true;
    public static List<Thread> threads = new ArrayList<Thread>();
    public static List<Connection> clients = new ArrayList<Connection>();

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

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

//      Make a thread that detects if the thread count has changed and redistribute that info

        while (running) {
            try {
                Socket socket = serverSocket.accept();
                System.out.println("New connection from " + socket.getInetAddress());
                Thread newThread = new Thread(new Connection(socket));
                threads.add(newThread);
                newThread.start();

                Thread.sleep(10000);
                break;
            } catch (IOException ex) {
                System.out.println("Can't accept client connection. ");
            }
        }
        serverSocket.close();
    }

}

and finally here is my class that handles each connection*:

public class Connection implements Runnable {

    private Socket socket;
    private InputStream input;
    private PrintWriter printer;
    private BufferedReader reader;

    private boolean running = true;
    private long ping = System.nanoTime() / 1000000;

    public Connection(Socket socket) {
        this.socket = socket;
        try {
            input = socket.getInputStream();
            printer = new PrintWriter(socket.getOutputStream(), true);
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException ex) {
            System.out.println("Can't get socket input/output stream.");
        }

    }

    @Override
    public void run() {
        String str;
        connect();
        try {
            while (Server.running && running && (str = reader.readLine()) != null) {
                handle(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        close();
    }

    private void connect() {

    }

    private void handle(String str) {
        if (str.equals("ping")) {
            send("pong");
        } else if (str.equals("pong")) {
            ping = (System.nanoTime() / 1000000) - ping;
            System.out.println("Ping: " + ping + "ms");
            send(SocketKey.CLOSE_CONNECTION);
        }

        if (str.equals(SocketKey.CLOSE_CONNECTION)) {
            running = false;
            System.out.println("Server Closing.");
            printer.println(SocketKey.CLOSE_CONNECTION);
        }

        if (str.startsWith("new_file:")) {
            receiveFile(str);
        }
    }

    public boolean receiveFile(String str) {

        System.out.print("Receiving file");
        long startTime = (System.nanoTime() / 1000000);

        String fileName = str.substring(str.indexOf("[") + 1, str.indexOf("]"));
        fileName = checkFileName(
                fileName.substring(0, fileName.lastIndexOf(".")) + fileName.substring(fileName.lastIndexOf(".")));
        long fileSize = Long.parseLong(str.substring(str.lastIndexOf("[") + 1, str.lastIndexOf("]")));
        OutputStream output = null;
        try {
            output = new FileOutputStream("./res/" + fileName);
        } catch (FileNotFoundException ex) {
            System.out.println("File not found. ");
        }
        byte[] bytes = new byte[16 * 1024];

        int count;
        long totalCount = 0, totalToDo = fileSize;
        double perc = 0;
        try {
            while ((count = input.read(bytes, 0, (int) Math.min(bytes.length, fileSize))) != -1 && 0 < fileSize) {
                fileSize -= count;
                output.write(bytes, 0, count);
                totalCount += count;
                if (perc < ((double) totalCount) / totalToDo) {
                    System.out.print(".");
                    perc += .1;
                }
                send(".");
            }
            output.close();
            long tdiff = ((System.nanoTime() / 1000000) - startTime);
            System.out.println("\nFile successfully saved. (" + tdiff + "ms)");
            System.out.println("Average transfer speed: " + Util.readableFileSize(totalToDo / (tdiff / 1000)) + "/s");
            Util.printFileInfo(new File("./res/" + fileName));
        } catch (IOException e) {
            send("Complete.");
            return false;
        }
        send("Complete.");
        return true;
    }

    public void close() {
        try {
            reader.close();
            printer.close();

            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String checkFileName(String fileName) {
        if (new File("./res/" + fileName).exists()) {
            if (fileName.contains("_")) {
                String hex = fileName.substring(fileName.lastIndexOf("_") + 1, fileName.lastIndexOf("."));
                return checkFileName(fileName.replace("_" + hex + ".",
                        "_" + Integer.toHexString(Integer.parseInt(hex, 16) + 1) + "."));

            }
            return checkFileName(fileName.substring(0, fileName.lastIndexOf(".")) + "_1"
                    + fileName.substring(fileName.lastIndexOf(".")));

        } else {
            return fileName;
        }

    }

    public void send(Object... objects) {
        String output = "";
        for (Object obj : objects) {
            output += obj.toString();
        }
        printer.println(output);
    }
}

Here is my Util class in case it is needed:

public class Util {
    public static final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    public static final int width = (int) screenSize.getWidth(), height = (int) screenSize.getHeight();

    public static String lower_alphabet = "abcdefghijklmnopqrstuvwxyz", upper_alphabet = "ABCDEFGHJKLMNOPQRSTUVWXYZ",
            digits = "0123456789", hex = "0123456789abcdef";

    public static JFrame getDefaultFrame() {
        JFrame frame = new JFrame();
        frame.setTitle("DataDrop");
        frame.setResizable(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        return frame;
    }

    public static boolean isDigit(String s) {
        return digits.contains(s);
    }

    public static boolean areDigits(String s) {
        for (char c : s.toCharArray())
            if (!isDigit(c + ""))
                return false;
        return true;
    }

    public static boolean isHex(String s) {
        return hex.contains(s);
    }

    public static boolean areHex(String s) {
        for (char c : s.toCharArray())
            if (!isHex(c + ""))
                return false;
        return true;
    }

    public static boolean isLetter(String s) {
        return lower_alphabet.contains(s) || upper_alphabet.contains(s);
    }

    public static boolean areLetters(String s) {
        for (char c : s.toCharArray())
            if (!isLetter(c + ""))
                return false;
        return true;
    }

    public static String readableFileSize(long size) {
        if (size <= 0)
            return "0";
        final String[] units = new String[] { "B", "kB", "MB", "GB", "TB" };
        int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
        return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    }

    public static void printFileInfo(File file) {
        String fileName = file.getName();
        System.out.println("File info:");
        System.out.println("    File Name     : " + fileName.substring(0, fileName.lastIndexOf(".")));
        System.out.println("    File Size     : " + readableFileSize(file.length()));
        System.out.println("    File Extension: " + fileName.substring(fileName.lastIndexOf(".")));
        System.out.println("    Full Path     : " + file.getAbsolutePath());
    }

}

*I removed the getters and setters because they are just the auto-generated ones from eclipse and they have no other function.

I am unsure of the efficiency of my transfer methods and I am pretty sure it's bad practice to send both bytes and strings through the socket, but I do not know another way.

I also don't know how to send more than one file without making the thread sleep for a second. If I don't let the Thread sleep, the program eventually gets hung up because a packet gets lost somehow. I know it shouldn't be possible, but I managed to find a way.

\$\endgroup\$
0
\$\begingroup\$

You need to use some kind wire protocol. In simplest case a triple (int length, int/enum for tag/type, byte[] data). Then you will be able to differentiate between command "ping" and a sending a file with content "ping". Also look into ProtoBuf or ObjectInputStream - it is futile to try parsing objects from toString() output.

\$\endgroup\$
  • \$\begingroup\$ Thank you for trying to help me, could you go into a little more detail about what a triple is? I didn't learn what an enum was in my APCS class, and I have never used one. \$\endgroup\$ – Ben Morrison Jul 25 at 19:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.