Transferencia de arquivos maiores que 4096 bytes via sockets

                                                                                            "Necessidade"

Olá Pessoal, estou com um pequeno grande problema com sockets que é o seguinte:
Tenho um cliente com um programa de loja desktop e coloquei um módulo nesse programa para o cliente fazer backup do banco de dados.
Até aí beleza, o cliente clica la pra salvar o backup, o arquivo sql é gerado tudo beleza. Agora estou tentando implementar um meio de quando o cliente
clicar pra fazer o backup, por meio de sockets ele se conectar com uma maquina servidor que tenho e enviar pela rede esse arquivo de backup do banco de dados para o meu servidor,
de modo que além do backup ficar salvo na maquina dele, o backup seja transferido para o meu servidor também.

                                                                                             "Implementação"

Daí, busquei ajuda na net, mais um pouquinho de conhecimento que tinha também, cheguei num código fonte que faz essa transferencia via sockets pra mim, porém testando aqui
na minha maquina eu transfiro arquivos de texto com menos de 4096 bytes com perfeição e rapidez, mas, quando o arquivo se trata do backup do banco de dados que tem em média
20480 bytes é lançada uma exceção que diz o seguinte:

Out 21, 2012 5:36:22 AM MenuP.Principal enviaParaOServidor Grave: null java.net.ConnectException: Connection timed out: connect

Então agora recorro a vocês, para tentarem me ajudar a melhorar esse código de forma que ele aceite transferencia acima de 4096, ou o que estou fazendo de errado nisso tudo,
segue abaixo os códigos cliente/servidor:

OBS: Notem que nos meus 2 vetores de bytes está comentado 4kb, isso é porque antes eu tinha implementado os vetores de tamanho 4096, mas como 4096 não é o tamanho que me serve aumentei para o tamango de 20kb na tentativa de acerto, mas não funcionou e deu o erro citado acima de perda de conexão por demora.

Cliente

FileInputStream in = null; try { File f = new File(Arquivo); in = new FileInputStream(f); Socket socket = new Socket("192.168.2.10", 5678); OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out); BufferedWriter writer = new BufferedWriter(osw); writer.write(f.getName() + "\n"); writer.flush(); int tamanho = 20480; // buffer de 4KB byte[] buffer = new byte[tamanho]; int lidos = -1; while ((lidos = in.read(buffer, 0, tamanho)) != -1) { out.write(buffer, 0, lidos); } } catch (UnknownHostException ex) { Logger.getLogger(Principal.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(Principal.class.getName()).log(Level.SEVERE, null, ex); }

Servidor

[code]try {
ServerSocket server = new ServerSocket(5678);
Socket clSocket = server.accept();
InputStream in = clSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader reader = new BufferedReader(isr);
String fName = reader.readLine();
System.out.println(fName);
File f1 = new File(“CaminhoDaMinhaMaquinaServidor\ArquiNovo.sql”);
FileOutputStream out = new FileOutputStream(f1);

            int tamanho = 20480; // buffer de 4KB    
            byte[] buffer = new byte[tamanho];    
            int lidos = -1;    
            while ((lidos = in.read(buffer, 0, tamanho)) != -1) {    
                System.out.println(lidos);  
                out.write(buffer, 0, lidos);    
            }    
            out.flush();    
    } catch (IOException e) {  
    }[/code]

Desde já fico muito agradecido, Abraços

x(

Seu código tem os seguintes problemas:

a) Cadê o close do socket?
b) Normalmente você também precisa usar shutdown e outros métodos de sockets
c) read(), se retornar -1, não indica necessariamente que o outro lado desistiu de mandar.

O correto é sempre mandar o tamanho dos dados a serem enviados antes dos dados, para que o lado que vai receber os dados espere exatamente a quantidade que foi acertada entre as partes. Não adianta tentar economizar 4 bytes aqui :frowning:

[quote=entanglement]Seu código tem os seguintes problemas:

a) Cadê o close do socket?
b) Normalmente você também precisa usar shutdown e outros métodos de sockets
c) read(), se retornar -1, não indica necessariamente que o outro lado desistiu de mandar.

O correto é sempre mandar o tamanho dos dados a serem enviados antes dos dados, para que o lado que vai receber os dados espere exatamente a quantidade que foi acertada entre as partes. Não adianta tentar economizar 4 bytes aqui :frowning:
[/quote]

Não entendi muito bem a parte de usar shutdown e enviar os dados de tamanho de arquivo antes de enviar o arquivo x(

Vê se isso te ilumina:

Que tal usar o ZeroMQ ( http://www.zeromq.org/ ) ? Ele resolve muitos desses problemas para você. A API é em C mas tem um binding para Java.

Resolvido o problema, porém não foi facil de achar segue o código cliente

try { FileInputStream in = null; File f = new File(Arquivo); in = new FileInputStream(f); Socket socket = new Socket("IPServidor", 5678); OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out); BufferedWriter writer = new BufferedWriter(osw); Thread.sleep(1000); writer.write(f.getName() + "\n"); writer.flush(); int tamanho = 20480; // buffer de 4KB byte[] buffer = new byte[tamanho]; int lidos = -1; while ((lidos = in.read(buffer, 0, tamanho)) != -1) { out.write(buffer, 0, lidos); } in.close(); socket.close(); out.close(); osw.close(); writer.close(); } catch (InterruptedException ex) { Logger.getLogger(Principal.class.getName()).log(Level.SEVERE, null, ex); } catch (UnknownHostException ex) { JOptionPane.showMessageDialog(null, "Cliente - " + ex); } catch (IOException ex) { JOptionPane.showMessageDialog(null, "Cliente - " + ex); }

por algum motivo que sinceramente eu não posso dizer por que acontece, mais essa linha aki:

 out.write(buffer, 0, lidos);

estava sendo executada antes das alinhas anteriores, sei que isso tem relação com thread e a pilha de chamadas então na linha 9 chamei um sleep de 1 segundo pra dar tempo das linhas anteriores serem executadas e realmente deu certo, tranferiu o arquivo que foi uma beleza. Fico muito grato a ajuda e realmente entanglement eu tinha me esquecido de chamar os closes()'s das minhas instancias que criei obrigado por me dar esse toque. E obrigado aí ViniGodoy realmente me iluminou o link que mandou obrigado

Agora que eu li direito o seu programa, eu percebi que ele é um programa do tipo “chatty” (ou seja, manda um comando e espera uma resposta).

Quando você tem um programa desses, é necessário lembrar que o TCP , se não configurado corretamente, usa o algoritmo de Nagle.

Basicamente, ele funciona assim: ele espera você juntar vários pacotinhos pequenos e o TCP manda tudo em um só pacote (princípio da “lotação do ônibus” - em vez de o ônibus sair na hora que chega um cliente, ele só sai quando lota o ônibus ou então chega a um horário determinado).

Usar “flush” no socket faz com que o buffer do Java seja jogado no sistema operacional, mas fica a cargo do stack de rede (no sistema operacional) se ele vai enviar imediatamente a mensagem, ou então se ele vai esperar até lotar o pacote. Ou seja: seu programa funcionou porque passou aquele tempo (1 segundo), mas você não tinha entendido por quê.

É necessário desligar esse algoritmo para você trabalhar com algoritmos do tipo “chatty” ( comando / resposta ).

Você desliga o algoritmo de Nagle, usando o comando setTcpNoDelay (true) no socket, imediatamente depois de abri-lo e antes de fazer qualquer coisa (ler ou escrever no socket).

http://docs.oracle.com/javase/6/docs/api/java/net/Socket.html#setTcpNoDelay(boolean)