Author Topic: Proeblema en transferencia de archivos  (Read 138 times)

Jaia

  • Newbie
  • *
  • Posts: 5
  • Karma: 0
    • View Profile
Proeblema en transferencia de archivos
« on: Enero 13, 2019, 10:41:09 pm »
Hola:

Estoy con un programa que se ejecuta una parte en el cliente y otra en el servidor. Quiero conectar ambas aplicaciones para que puedan realizar una transferencia de archivos. Después de pelear con el código y de los BrokenPipe y los Reset que me lanzaba, ya he conseguido que se conecten.

Ahora el problema surge cuando quiero transferir archivos de un sitio a otro. Al finalizar la transferencia me doy cuenta de que el archivo recibido es menor que el transferido. Es decir, que si el archivo original pesa 3MB el servidor lee y almacena 2.7MB.

En el lado del servidor tengo:
Code: [Select]
public class CmdThread extends Thread {

    private final Socket SO;
    private final BufferedReader entrada;
    private final BufferedOutputStream salida;
    private String peticion, cmd, nombreArchivo;
    private Long tam;

    public CmdThread(Socket SO) throws IOException {
        this.SO = SO;
        if (this.SO != null) {
            entrada = new BufferedReader(new InputStreamReader(this.SO.getInputStream()));
            salida = new BufferedOutputStream(this.SO.getOutputStream());
        } else {
            entrada = null;
            salida = null;
        }
    }

    @Override
    public void run() {
        try {
            peticion = entrada.readLine();
            System.out.println("Peticion: " + peticion);
            String[] str = peticion.split("&");
            System.out.println("Paramns: " + Arrays.toString(str));
            cmd = str[0];
            nombreArchivo = str[1];
            tam = Long.valueOf(str[2]);
            BufferedOutputStream salidaDisco = new BufferedOutputStream(new FileOutputStream(new File(nombreArchivo)));
            while (entrada.ready()) {
                salidaDisco.write(entrada.read());
            }
            salida.flush();
            salida.close();
            salidaDisco.flush();
            salidaDisco.close();
            entrada.close();
        } catch (IOException ex) {
            Logger.getLogger(CmdThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

Y en el lado del cliente esto:
Code: [Select]
public class ArchivosCliente {

    public static void main(String[] args) throws IOException {
        JFileChooser selector = new JFileChooser();
        selector.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int res = selector.showOpenDialog(selector);
        if (res == JFileChooser.APPROVE_OPTION) {
            File fila = selector.getSelectedFile();
            String comando = "UP";
            String del = "&";
            String nombreArchivo = fila.getName();
            String remoto = "127.0.0.1";
            int puerto = 57000;
            String tam = String.valueOf(fila.length());
            Socket so = new Socket(remoto, puerto);
            so.setSoLinger(true, 5);
            BufferedOutputStream salida = new BufferedOutputStream(so.getOutputStream());
            BufferedInputStream entrada = new BufferedInputStream(new FileInputStream(fila));
            System.out.println("Peticion: " + comando + del + nombreArchivo + del + tam + "\n");
            salida.write((comando + del + nombreArchivo + del + tam + "\n").getBytes());
            int n;
            while ((n = entrada.read()) != -1) {
                salida.write(n);
            }
            salida.flush();
            salida.close();
            entrada.close();
        }
    }

}

No entiendo en dónde se encuentra el error. No hay ninguna excepción y alerta ni ninguna pista que me ayude a tirar del hilo para averiguar el error o la causa del problema.

Alguna sugerencia?

chuidiang

  • Administrator
  • Hero Member
  • *****
  • Posts: 5451
  • Karma: 12
    • View Profile
    • Apuntes de programación
Re: Proeblema en transferencia de archivos
« Reply #1 on: Enero 14, 2019, 05:30:44 pm »
Hola:
Si haces varias pruebas y transmites el mismo fichero varias veces ... ¿siempre tiene el mismo tamaño lo que recibes o cada vez es distinto?
Veo que escribes byte a byte y que lees byte a byte con el ready(). Si el que escribe no lo hace a la suficiente velocidad, es posible que se te corte en cualquier momento. El efecto debería ser que a veces va, a veces no y cuando no va, el fichero recibido será más pequeño, pero unas veces tendrá un tamaño y otras otro.
Saludos.

Jaia

  • Newbie
  • *
  • Posts: 5
  • Karma: 0
    • View Profile
Re: Proeblema en transferencia de archivos
« Reply #2 on: Enero 14, 2019, 08:40:37 pm »
Bueno he realizado algunos cambios pero realmente no sé lo que está pasando.

Tenías razón sobre lo del buffer y conseguí resolver el problema en parte. Ahora me escribe todos los bytes, por lo menos en lo que a la cantidad se refiere, pero escribe lo que quiere ya que es ilegible.

Abrir el archivo directamente con alguna aplicación es imposible, no lo reconoce, y al abrir el archivo con un editor hexadecimal, y comparar el original y el transferido, pues veo que no se parecen en nada.

En el servidor tengo esto:

Code: [Select]
@Override
    public void run() {
        try {
            entradaStr = new BufferedReader(new InputStreamReader(this.so.getInputStream()));
            entrada = new DataInputStream(new BufferedInputStream(this.so.getInputStream()));
            String linea = entradaStr.readLine();
            System.out.println("Linea: " + linea);
            String[] str = linea.split("&");
            System.out.println("str: " + Arrays.toString(str));
            for (String s : str) {
                System.out.println(s);
            }
            String comando = str[0];
            String nombre = str[1];
            Long tam = Long.valueOf(str[2].trim());
            salida = new DataOutputStream(new FileOutputStream(new File(nombre)));
            byte[] b = new byte[8192];
            int n;
            while ((n = entrada.read(b)) != -1) {
                salida.write(b);
            }
            entradaStr.close();
            entrada.close();
            salida.flush();
            salida.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
            System.exit(0);
        }
        System.exit(0);
    }

Y en el cliente:
Code: [Select]
public static void main(String[] args) {
        JFileChooser selector = new JFileChooser();
        selector.setFileSelectionMode(JFileChooser.FILES_ONLY);
        selector.setMultiSelectionEnabled(false);
        int res = selector.showOpenDialog(selector);
        if (res == JFileChooser.APPROVE_OPTION) {
            File fila = selector.getSelectedFile();
            String nombre = fila.getName();
            String tam = String.valueOf(fila.length());
            String comando = "UP";
            String remoto = "127.0.0.1";
            String del = "&";
            int puerto = 57000;
            try {
                Socket so = new Socket(remoto, puerto);
                so.setSoLinger(true, 5);
                String linea = comando + del + nombre + del + tam + "\n";
                DataInputStream entrada = new DataInputStream(new FileInputStream(fila));
                DataOutputStream salida = new DataOutputStream(new BufferedOutputStream(so.getOutputStream()));
                salida.writeBytes(linea);
                int n;
                byte[] b = new byte[8192];
                while ((n = entrada.read(b)) != -1) {
                    salida.write(b);
                }
                entrada.close();
                salida.flush();
                salida.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
           
        }
    }

He puesto un buffer como me indicabas pero ya digo que no comprendo como hacer esto de forma correcta.

No estoy obligado a utilizar esta fórmula ni mucho menos, tengo flexibilidad total, pero me llega al alma no comprender algo que en principio es sencillo, y que aparentemente sobre el papel es correcto.

No lo comprendo.

chuidiang

  • Administrator
  • Hero Member
  • *****
  • Posts: 5451
  • Karma: 12
    • View Profile
    • Apuntes de programación
Re: Proeblema en transferencia de archivos
« Reply #3 on: Enero 15, 2019, 04:21:55 pm »
Hola:

Cuando lees un array (   n = entrada.read(b) ) no tienes garantía de leer TODOS los bytes del array, sobre todo en socket que los bytes circulan por red y  pueden no haber llegado todavía todos los bytes necesarios para rellenar el array al completo. De hecho, n te está diciendo cuántos bytes has leído y debes usar ese valor de n para saber cuántos bytes hay en el array que han sido leidos.

Deberías hacer algo parecido a esto

Code: [Select]
byte[] b = new byte[8192];
                while ((n = entrada.read(b)) != -1) {
                    salida.write(b, 0, n);   // bytes, empezando en el 0, hasta n
                }

También podias dejarlo como lo tenias, bytes de uno en uno, pero lo que no puedes hacer es usar entrada.ready() para decidir que has llegado al final del socket. Si el byte no ha llegado cuando preguntas, ready() te dira que no hay byte, pero no porque se hayan terminado de enviar, sino porque solo porque no ha llegado todavía.

Saludos.

Jaia

  • Newbie
  • *
  • Posts: 5
  • Karma: 0
    • View Profile
Re: Proeblema en transferencia de archivos
« Reply #4 on: Enero 15, 2019, 04:31:08 pm »
Este tipo de explicaciones son las que deberían venir en los libros.

Muchísimas gracias.

 

ey