Отправка файла с помощью сокетов Java, потеря данных

Я пытаюсь отправить файл с клиента на сервер с сокетами в Java. Он отлично работает, когда я тестирую на одной машине, но когда я тестирую на разных машинах, я теряю большие куски данных, что приводит к повреждению файла. Если я попытаюсь отправить очень маленький файл (‹20 байт), он даже не достигнет println внутри цикла while сервера.

Вот мой код:

Server.java

package edu.mst.cs.sensorreceiver;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static final int PORT = 51111;
    private static final int CHUNK_SIZE = 1024;
    private static final File _downloadDir = new File("downloads/");

    public static void main(String[] args) {
        if (!_downloadDir.exists()) {
            if (!_downloadDir.mkdirs()) {
                System.err.println("Error: Could not create download directory");
            }
        }

        Socket socket = null;
        try {
            ServerSocket server = new ServerSocket(PORT);

            while (true) {
                System.out.println("Waiting for connection...");
                socket = server.accept();

                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                String name = in.readLine();
                File file = new File(_downloadDir, name);

                String size = in.readLine();
                int fileSize;
                try {
                    fileSize = Integer.parseInt(size);
                } catch (NumberFormatException e) {
                    System.err.println("Error: Malformed file size:" + size);
                    e.printStackTrace();
                    return;
                }

                System.out.println("Saving " + file + " from user... (" + fileSize + " bytes)");
                saveFile(file, socket.getInputStream());
                System.out.println("Finished downloading " + file + " from user.");
                if (file.length() != fileSize) {
                    System.err.println("Error: file incomplete");
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void saveFile(File file, InputStream inStream) {
        FileOutputStream fileOut = null;
        try {
            fileOut = new FileOutputStream(file);

            byte[] buffer = new byte[CHUNK_SIZE];
            int bytesRead;
            int pos = 0;
            while ((bytesRead = inStream.read(buffer, 0, CHUNK_SIZE)) >= 0) {
                pos += bytesRead;
                System.out.println(pos + " bytes (" + bytesRead + " bytes read)");
                fileOut.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileOut != null) {
                try {
                    fileOut.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("Finished, filesize = " + file.length());
    }
}

Клиент.java

package edu.mst.cs.sensorreceiver;

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

public class Client {
    private static final String HOSTNAME = "131.151.163.153";
    private static final int PORT = 51111;
    private static final int CHUNK_SIZE = 1024;

    public static void main(String[] args) {
        sendFile(args[0]);
    }

    private static void sendFile(String path) {
        if (path == null) {
            throw new NullPointerException("Path is null");
        }

        File file = new File(path);
        Socket socket = null;
        try {
            System.out.println("Connecting to server...");
            socket = new Socket(HOSTNAME, PORT);
            System.out.println("Connected to server at " + socket.getInetAddress());

            PrintStream out = new PrintStream(socket.getOutputStream(), true);

            out.println(file.getName());
            out.println(file.length());

            System.out.println("Sending " + file.getName() + " (" + file.length() + " bytes) to server...");
            writeFile(file, socket.getOutputStream());
            System.out.println("Finished sending " + file.getName() + " to server");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void writeFile(File file, OutputStream outStream) {
        FileInputStream reader = null;
        try {
            reader = new FileInputStream(file);
            byte[] buffer = new byte[CHUNK_SIZE];
            int pos = 0;
            int bytesRead;
            while ((bytesRead = reader.read(buffer, 0, CHUNK_SIZE)) >= 0) {
                outStream.write(buffer, 0, bytesRead);
                outStream.flush();
                pos += bytesRead;
                System.out.println(pos + " bytes (" + bytesRead + " bytes read)");
            }
        } catch (IndexOutOfBoundsException e) {
            System.err.println("Error while reading file");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Error while writing " + file.toString() + " to output stream");
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Я работал над этим часами и почти не продвинулся. Я думал, что довольно хорошо понимаю, как работает чтение/запись из потоков, но явно что-то здесь упускаю.

Опять же, все работает отлично, когда я отправляю и получаю с одной и той же машины. Но если я попытаюсь отправить файл между двумя компьютерами, даже если они находятся в одной локальной сети, я потеряю большую часть отправленных данных.

Может ли кто-нибудь понять мою проблему? Я уже перепробовал все, что мог придумать.


person 0x5453    schedule 26.10.2014    source источник


Ответы (2)


Похоже, вы смешиваете разрозненные данные и операции, ориентированные на строки. Я предлагаю вам использовать DataInputStream на сервере и DataOutputStream. Начиная с клиента, что-то вроде

private static void sendFile(String path) {
    if (path == null) {
        throw new NullPointerException("Path is null");
    }

    File file = new File(path);
    Socket socket = null;
    try {
        System.out.println("Connecting to server...");
        socket = new Socket(HOSTNAME, PORT);
        System.out.println("Connected to server at "
                + socket.getInetAddress());

        try (DataOutputStream dos = new DataOutputStream(
                new BufferedOutputStream(socket.getOutputStream()));) {
            dos.writeUTF(file.getName());
            dos.writeLong(file.length());

            System.out.println("Sending " + file.getName() + " ("
                    + file.length() + " bytes) to server...");
            writeFile(file, dos);
            System.out.println("Finished sending " + file.getName()
                    + " to server");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Потом на сервере

socket = server.accept();

DataInputStream dis = new DataInputStream(socket.getInputStream());
String name = dis.readUTF();
File file = new File(_downloadDir, name);
long fileSize = dis.readLong();
System.out.println("Saving " + file + " from user... ("
        + fileSize + " bytes)");
person Elliott Frisch    schedule 26.10.2014
comment
Вау, ты прав, это исправило это. Я не осознавал, что смешивание различных типов потоков может быть настолько проблематичным, хотя теперь, когда я об этом думаю, это имеет смысл. Спасибо, я больше не совершу этой ошибки. - person 0x5453; 26.10.2014

В вашем коде вы смешиваете все, что никогда не следует смешивать: (а) потоки ввода/вывода, (б) PrintStream, (в) потоковое чтение и (г) буферизованное чтение. этот ад приводит к описанному поведению.

вы должны использовать единственный объект (поток) для записи всего вашего персонала. и единственный объект (поток) для чтения всех ваших сотрудников.

Я могу порекомендовать вам использовать либо

OutputStream out = new BufferedOutputStream(socket.getOutputStream()); // for writing
InputStream in = new BufferedInputStream(socket.getInputStream()); // for reading

or

DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

Первый более гибкий, второй проще в использовании.

person ursa    schedule 26.10.2014