Problema com transferencia de arquivo via socket

Olá a todos,
Eu estou tentando fazer um programa cliente/servidor que aceite mais de um cliente e esse cliente envia arquivos para o servidor.
A transferência estava funcionando corretamente, porém quando eu coloquei um while para que ao enviar uma vez ele possa dar a opção de enviar outra vez ou sair, acabou dando erro no recebimento do arquivo. Não estou conseguindo resolver esse problema, alguém poderia me ajudar?
A baixo estou enviando o código completo com a saída que eu pus para testar quanto estava sendo enviado e lido.

Cliente:

public class AplicaçãoCliente {
    
    public static void main(String[] args) throws UnknownHostException, IOException {
        
        Scanner scan = new Scanner(System.in);
        
        System.out.println("informe ip:");
        
        String ip = scan.nextLine();
        
        System.out.println("informe porta:");
        
        int porta = scan.nextInt();
        
        Socket socket = new Socket(ip, porta);

        System.out.println("O cliente se conectou ao servidor!");
        
        new Cliente(socket).conectar();
        
        System.out.println("fim da execução");
    }
}


public class Cliente {
    
    private Socket cliente;
    private PrintStream saida;
    private Scanner teclado;
    

    public Cliente(Socket cliente) {

        this.cliente = cliente;
        
    }
    
    
    public void conectar() throws IOException{

    
        teclado = new Scanner(System.in);
        saida = new PrintStream(cliente.getOutputStream());
        String continuar = "s";
        
        System.out.println("------------------BEM-VINDO AO SERVIDOR------------------");
        System.out.println();
        
        while(continuar.equals("s")){//while adicionado que fez dar o erro
        
            System.out.println();
            System.out.println("Digite comando:");
        
    
        
            String comando = teclado.nextLine();
    
        
            saida.println(comando);
        
            if(comando.equals("upload")){
            
                enviarArquivo("/home/felipe/document.pdf");
            }else if(comando.equals("sair")){
                continuar = "n";
            }else{
                System.out.println("comando não existente");
            }
        }
        cliente.close();
    }
    
    private void enviarArquivo(String caminho) throws IOException{
        
        FileInputStream fileIn = new FileInputStream(caminho);//caminho do arquivo que sera enviado

        OutputStream out = cliente.getOutputStream();

        int tam = 4096;
        byte[] buffer = new byte[tam];
        int lidos = 0;
        int i = 0;
        
        lidos = fileIn.read(buffer, 0, tam);
        System.out.println(i++ + " - lidos: " + lidos);
        while (lidos > 0) {
            
            out.write(buffer, 0, lidos);
            lidos = fileIn.read(buffer, 0, tam);
            System.out.println(i++ + " - lidos: " + lidos);
        }
        
        out.flush();
        fileIn.close();

        System.out.println("enviado com sucesso");
        
    }
}

Servidor:

public class AplicaçãoServidor {
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        // inicia o servidor
        Servidor servidor = new Servidor(12345);
    
        servidor.executa();
    
        System.out.println("fim");
    }
}

public class Servidor {
    
    private int porta;
    
    private ServerSocket servidor;
    
    public Servidor(int porta) {

        this.porta = porta;
        
    }

    public void executa() throws IOException {
        

        Socket cliente;
        servidor = new ServerSocket(this.porta);
        
        System.out.println("Porta 12345 aberta!");
        
        while(true){
                
            // aceita um cliente

            cliente = servidor.accept();

            System.out.println("Nova conexão com o cliente " +

            cliente.getInetAddress().getHostAddress());
            
            
            
            // cria tratador de cliente numa nova thread

            ThreadServidor thread = new ThreadServidor(cliente);

            new Thread(thread).start();
            
            

        }
    }
        
}

public class ThreadServidor implements Runnable {

    private Socket cliente;
    
    private Scanner entrada;

    
        public ThreadServidor(Socket cliente) {

        this.cliente = cliente;
    

    }

    public void run() {
        
        try {
            
            entrada = new Scanner(this.cliente.getInputStream());
            String continuar = "s";
            
            while(continuar.equals("s")){// while adicionado que originou o erro

                String resposta = entrada.nextLine();
            
                if(resposta.equals("upload")){
                    receberArquivo("/home/felipe/Downloads/documento.pdf");
                }else if(resposta.equals("sair")){
                    continuar = "n";
                }else{
                    System.out.println("comando não existente");
                }
            }
            cliente.close();
        } catch (IOException e) {
            
            e.printStackTrace();
        }
            
    }
    
    public void receberArquivo(String caminho) throws IOException {
        
        FileOutputStream fos = new FileOutputStream(caminho);// o caminho onde o arquivo sera escrito
        
        int tam = 4096;
        byte[] buffer = new byte[tam];
        int lidos = 0;
        
        InputStream in = cliente.getInputStream();
        
        int i = 0;
        
         
        lidos = in.read(buffer, 0, tam);
        System.out.println(i++ + " - lidos: " + lidos);
        
        while (lidos > 0) {
            
            fos.write(buffer, 0, lidos);
            lidos = in.read(buffer, 0, tam);
            System.out.println(i++ + " - lidos: " + lidos);
        }
        
        fos.flush();
        fos.close();
        
        System.out.println("arquivo recebido");
            
        
    }
    
    
}

Saída cliente:

O cliente se conectou ao servidor!
------------------BEM-VINDO AO SERVIDOR------------------


Digite comando:
upload
0 - lidos: 4096
1 - lidos: 4096
2 - lidos: 4096
3 - lidos: 4096
4 - lidos: 4096
5 - lidos: 4096
6 - lidos: 4096
7 - lidos: 4096
8 - lidos: 4096
9 - lidos: 4096
10 - lidos: 4096
11 - lidos: 4096
12 - lidos: 4096
13 - lidos: 4096
14 - lidos: 4096
15 - lidos: 4096
16 - lidos: 4096
17 - lidos: 4096
18 - lidos: 4096
19 - lidos: 4096
20 - lidos: 4096
21 - lidos: 4096
22 - lidos: 4096
23 - lidos: 4096
24 - lidos: 4096
25 - lidos: 4096
26 - lidos: 4096
27 - lidos: 4096
28 - lidos: 4096
29 - lidos: 4096
30 - lidos: 4096
31 - lidos: 4096
32 - lidos: 4096
33 - lidos: 4096
34 - lidos: 4096
35 - lidos: 4096
36 - lidos: 4096
37 - lidos: 4096
38 - lidos: 4096
39 - lidos: 4096
40 - lidos: 4096
41 - lidos: 4096
42 - lidos: 4096
43 - lidos: 4096
44 - lidos: 4096
45 - lidos: 4096
46 - lidos: 4096
47 - lidos: 4096
48 - lidos: 4096
49 - lidos: 4096
50 - lidos: 4096
51 - lidos: 3698
52 - lidos: -1
enviado com sucesso

Digite comando:

Saída Servidor:

0 - lidos: 4096
1 - lidos: 4096
2 - lidos: 4096
3 - lidos: 4096
4 - lidos: 4096
5 - lidos: 4096
6 - lidos: 4096
7 - lidos: 4096
8 - lidos: 4096
9 - lidos: 4096
10 - lidos: 4096
11 - lidos: 4096
12 - lidos: 4096
13 - lidos: 4096
14 - lidos: 4096
15 - lidos: 4096
16 - lidos: 4096
17 - lidos: 4096
18 - lidos: 4096
19 - lidos: 4096
20 - lidos: 4096
21 - lidos: 4096
22 - lidos: 4096
23 - lidos: 4096
24 - lidos: 4096
25 - lidos: 4096
26 - lidos: 4096
27 - lidos: 4096
28 - lidos: 4096
29 - lidos: 4096
30 - lidos: 4096
31 - lidos: 4096
32 - lidos: 4096
33 - lidos: 4096
34 - lidos: 4096
35 - lidos: 4096
36 - lidos: 4096
37 - lidos: 4096
38 - lidos: 4096
39 - lidos: 4096
40 - lidos: 4096
41 - lidos: 4096
42 - lidos: 4096
43 - lidos: 4096
44 - lidos: 4096
45 - lidos: 4096
46 - lidos: 4096
47 - lidos: 4096
48 - lidos: 4096
49 - lidos: 4096
50 - lidos: 4096
51 - lidos: 3698

//Ele não saiu do while. Ele não recebeu o -1 que estava recebendo antes para visar fim do arquivo.

na classe ThreadServidor, dentro do método receberArquivo troca

   while (lidos > 0) {
         fos.write(buffer, 0, lidos);
            lidos = in.read(buffer, 0, tam);
            System.out.println(i++ + " - lidos: " + lidos);
        }

por

do  {
	fos.write(buffer, 0, lidos);
	lidos = in.read(buffer, 0, tam);
	System.out.println(i++ + " - lidos: " + lidos);
} while (lidos == 4096);

melhorou ?

(é que na verdade ele não precisa fazer isso “enquanto lidos for maior do que 0”… até por que MUITO dificilmente vai terminar em um número que seja igual a 0. Solução: já que a gente não sabe em que número exatamente ele deve parar então ele SÓ vai continuar se lidos for igual a 4096: Por que daí a gente sabe que ele PRECISA continuar…)

gostei muito da sua resposta… espero sempre encontrá-la por aqui

1 curtida

De nada.

Mas tu conseguiu?..

Sobre o método que você mostrou eu já havia testado e infelizmente não deu certo, porém depois de pesquisar mais eu consegui resolver. O problema era com a sincronização do método. Dessa forma eu só usei antes do método de enviar um Thread.sleep(200), pode até ser menos, não testei com menos.

Antes de descobrir isso eu mudei muita coisa, então não sei dizer se isso vai funcionar com esses métodos.

O meu metodo de enviarArquivo agora ficou:

public void sendFile(String fileName) throws IOException {
        try {
           
            File myFile = new File(fileName);
            byte[] mybytearray = new byte[(int) myFile.length()];

            FileInputStream fis = new FileInputStream(myFile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            

            DataInputStream dis = new DataInputStream(bis);
            dis.readFully(mybytearray, 0, mybytearray.length);

           
            OutputStream os = cliente.getOutputStream();

            
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(myFile.getName());
            dos.writeLong(mybytearray.length);
            dos.write(mybytearray, 0, mybytearray.length);
            dos.flush();
            
            System.out.println("Arquivo "+fileName+" enviado para cliente.");
       
        } catch (FileNotFoundException e) {
            System.err.println("Arquivo não existe!");
        } 
    }

O método de receberArquivo ficou:

    public void receiveFile(String caminho) throws IOException {
        try {
            int bytesRead;

            DataInputStream clientData = new DataInputStream(
                    cliente.getInputStream());
            
            
            String fileName = clientData.readUTF();
            String caminhoCompleto = caminho + "/" + fileName;
            
            OutputStream output = new FileOutputStream((caminhoCompleto));
            long size = clientData.readLong();
            byte[] buffer = new byte[1024];
            
            while (size > 0
                    && (bytesRead = clientData.read(buffer, 0,
                            (int) Math.min(buffer.length, size))) != -1) {
                output.write(buffer, 0, bytesRead);
                size -= bytesRead;
            }

            output.close();

            System.out.println("Arquivo " + fileName
                    + " recebido pelo cliente.");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("Arquivo não encontrado");
        }

    }