Manter o socket conectado após transferencia de arquivo

8 respostas
D

Boa tarde galera,

Sempre acompanho o forum, porém nunca tive uma duvida que valesse a pena vir aqui e levantar a vocês.

Hoje, estou desenvolvendo um cliente / servidor utilizando conexão socket. Nesse servidor, existe um protocolo que identifica a ação que quero que o servidor faça, exemplo:

Se o servidor ler o inteiro 1, ele envia string para o servidor executar comandos, se ler 2, o servidor se prepara para receber um arquivo.
Resumindo, não consigo manter a conexão ao enviar um arquivo, pois por algum motivo o sistema fica preso na leitura do buffer, então, eu preciso fechar a conexão para que a conexão não fique travada.

Não coloquei o codigo aqui, pois também não sei se existe alguma maneira de limpar o output e input sem resetar o socket.

Abraços e obrigado!

8 Respostas

D

Oops, acabei me empolgado e abri um topico na sessão errada. :confused:
Me desculpem!

Abraço!

V

Oi.

Por favor, não dê um SUPER DESTAQUE para coisas óbvias, como o fato de você ter dúvida. O resto do seu título já estava bem adequado. E deixe a CAIXA ALTA de lado.

Vou mover seu tópico para a sessão de Java Avançado.

Quanto à sua dúvida. Não há nada que obrigue você a fechar um socket após uma transmissão, se isso está ocorrendo, é por causa de algum bug. Sem ver seu código, e entender o protocolo de comunicação, fica bem difícil imaginar o que está acontecendo por aí.

Você já baixou o wireshark e deu uma olhada no que está acontecendo na conexão de rede?

W

Uma comunicação socket requer alguns cuidados para funcionar corretamente e isso deve ser em ambos os lados (client/server):

  1. ao registrar uma conexão socket, deve indicar que este deverá ser lido;
  2. quando você quiser enviar algo, deve verificar se o host está lendo;
  3. se ele estiver lendo, você deve alterar o estado do socket para indicar que pretende escrever nele;
  4. você escreve o buffer no socket para enviar ao host e verificar pelo retorno o quanto do buffer foi escrito (*);
  5. após terminar de escrever tudo, você deve indicar que pretende ler o socket, deste modo o host terá a oportunidade de “falar algo a qualquer momento” sem travar.

(*) Se o seu host não for muito rápido no processamento, você deve dar alguns milis de intervalo se tiver muitas mensagens a enviar, senão você gera um estouro da pilha de recebimento da placa de rede do host, semelhante a um afogamento.

O melhor é você postar junto com sua dúvida, o trecho do código onde você identificou o problema e ainda informar se está usando Socket ou SocketChannel.

wiliamps

D

ViniGodoy:
Oi.

Por favor, não dê um SUPER DESTAQUE para coisas óbvias, como o fato de você ter dúvida. O resto do seu título já estava bem adequado. E deixe a CAIXA ALTA de lado.

Vou mover seu tópico para a sessão de Java Avançado.

Quanto à sua dúvida. Não há nada que obrigue você a fechar um socket após uma transmissão, se isso está ocorrendo, é por causa de algum bug. Sem ver seu código, e entender o protocolo de comunicação, fica bem difícil imaginar o que está acontecendo por aí.

Você já baixou o wireshark e deu uma olhada no que está acontecendo na conexão de rede?

Desculpe pelas gafes.

D

wiliamps:
Uma comunicação socket requer alguns cuidados para funcionar corretamente e isso deve ser em ambos os lados (client/server):

  1. ao registrar uma conexão socket, deve indicar que este deverá ser lido;
  2. quando você quiser enviar algo, deve verificar se o host está lendo;
  3. se ele estiver lendo, você deve alterar o estado do socket para indicar que pretende escrever nele;
  4. você escreve o buffer no socket para enviar ao host e verificar pelo retorno o quanto do buffer foi escrito (*);
  5. após terminar de escrever tudo, você deve indicar que pretende ler o socket, deste modo o host terá a oportunidade de “falar algo a qualquer momento” sem travar.

(*) Se o seu host não for muito rápido no processamento, você deve dar alguns milis de intervalo se tiver muitas mensagens a enviar, senão você gera um estouro da pilha de recebimento da placa de rede do host, semelhante a um afogamento.

O melhor é você postar junto com sua dúvida, o trecho do código onde você identificou o problema e ainda informar se está usando Socket ou SocketChannel.

wiliamps
http://oracle2java.blogspot.com/

Valeu pelas informações wiliamps!

O codigo simplificado é esse:

Cliente.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;


public class Cliente {
public static void main(String[] args) throws UnknownHostException, IOException {
		
		Socket socket = null;
		Cliente cliente = new Cliente();
		socket = new Socket(InetAddress.getLocalHost(), 1000);
		
		DataOutputStream out = new DataOutputStream(socket.getOutputStream());
		DataInputStream in = new DataInputStream(socket.getInputStream());
		
		try {
			
			while(true){
			
				Scanner scanner = new Scanner(System.in);
			
				System.out.println("--DarkHorseClient--");
				System.out.println("-------Menu--------");
				System.out.println("1 - Enviar arquivo-");
				System.out.println("2 - Enviar comando-");
				System.out.println("0 - Sair-----------");
				int teste = scanner.nextInt();
				
				if(teste < 0 || teste > 3){
					System.out.println("Digito invalido!");
				}else{
								
					switch(teste){
					
						case 1:
							out.writeInt(teste);
							System.out.println(in.readUTF());
							System.out.println(in.readUTF());
							System.out.println(in.readUTF());
							
							new Cliente().enviarArquivo(socket, out, in);
						break;
						case 2:
							
							out.writeInt(teste);
							System.out.println(in.readUTF());
							System.out.println(in.readUTF());
							System.out.println(in.readUTF());
							
							cliente.enviarString(socket, out, in);
						break;
						case 0:
							socket.close();
							System.exit(0);
						break;
					
					}
				}
			}
		} catch (IOException e) {
			System.out.println("Não foi possivel se conectar ao host...");
		}
	}
	
	public void enviarArquivo(Socket socket, DataOutputStream out, DataInputStream input){
		
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("Caminho do arquivo: ");
		String diretorio = scanner.nextLine();
		
		System.out.println("Nome do arquivo: ");
		String nomeDoArquivo = scanner.nextLine();
		
		File f = new File(diretorio + nomeDoArquivo);
		
		try {
			
			System.out.println("Transferindo o arquivo: " + f.getName());
			out.writeUTF(f.getName());
			out.writeLong(f.length());
			FileInputStream in = new FileInputStream(f);
			byte[] buffer = new byte[4096];

			while(true) {
				int len = in.read(buffer);
				if(len == -1) break;
				out.write(buffer, 0, len);
			}  
			System.out.println("Arquivo enviado com sucesso!");
		} catch(Exception e) {
		}
	}
	
	public void enviarString(Socket socket, DataOutputStream out, DataInputStream in){
		
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("Digite o comando: ");
		String comando = scanner.nextLine();
		
		try {
			
			out.writeUTF(comando);
			
			System.out.println(in.readUTF());
			
			System.out.println(in.readUTF());
			
			System.out.println("Comando executado com sucesso!");
			
		} catch (IOException e) {
		}
		
	}
}

Servidor.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class Servidor {
	
	public static void main(String[] args) throws IOException {
	
	ServerSocket serverSocket = new ServerSocket(1000);
	Socket socket = new Socket();
	Servidor servidor = new Servidor();
	
	DataOutputStream out = new DataOutputStream(socket.getOutputStream());
	DataInputStream in = new DataInputStream(socket.getInputStream());
	
	while(true){
		
	try{
		
		System.out.println("Aguardando requisição...");
		socket = serverSocket.accept();
		
		
		out.writeUTF("Status: Conectado");
		out.writeUTF("Hostname: " + socket.getLocalAddress());
		out.writeUTF("Sistema Operacional: " + System.getProperty("os.name"));
		
		int teste = in.readInt();
		
			switch(teste){
				case 1:
					servidor.receberArquivos(socket, in, out);
				break;
				case 2:
					servidor.recebeString(socket, in, out);
				break;
				case 0:
					socket.close();
					System.exit(0);
				break;
					
			}
		
		} catch(Exception e){
		}
	
	}
}

public void recebeString(Socket socket, DataInputStream in, DataOutputStream out){
	try {
		
		String comando = in.readUTF();
		
		out.writeUTF("Executando comando enviado...");
		
		Process process = Runtime.getRuntime().exec(comando);
		InputStream inputStream = process.getInputStream();
		
		String saida = "";
		
		int n = 0;
		
		
		while ((n = inputStream.read()) != -1) {
			saida += (char)n; 
		}
		
		out.writeUTF(saida);
		System.out.println("Enviando saída do prompt...");
		
	} catch (IOException e) {
	}
}

public void receberArquivos(Socket socket, DataInputStream in, DataOutputStream out){
	try {
		System.out.println("Esperando por arquivos.");
		
		String nomeDoArquivo = in.readUTF();
		long size = in.readLong();
		
		System.out.println("Processando arquivo: " + nomeDoArquivo + " - " + size + " bytes.");

		FileOutputStream fos = new FileOutputStream(nomeDoArquivo);

		byte[] buffer = new byte[4096];

		while(true) {
			int len = in.read(buffer);
			if(len == -1){
				break;
			}else{
				fos.write(buffer, 0, len);
			}
		}
		
		fos.flush();
		fos.close();
		
		System.out.println("Arquivo ." + nomeDoArquivo + " recebido com sucesso.");
	} catch (Exception ex) {
	}
	
}
}

Eu fiz o debug e percebi que ele fica preso no while aguardando alguma informção, porém não existe mais e simplesmente ele continua tentando ler, porém não recebe mais parametro.

Muito obrigado!

W

Você não pode ler direto de FileInputStream.

Procura utilizar:

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

E ler de “bis”.

Verifica este link para entender:

http://www.java2s.com/Code/Java/Network-Protocol/TransferafileviaSocket.htm

Outra coisa, como você está lendo o arquivo em blocos e não inteiramente, utilizou um laço while.

Se o seu arquivo for grande demais, o while poderá consumir processamento só para ele e dar a impressão que sua máquina travou.

Faz o seguinte, coloca um Thread.sleep(5) só para emprestar processamento para outras aplicações ou threads.

D

wiliamps:
Você não pode ler direto de FileInputStream.

Procura utilizar:

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

E ler de “bis”.

Verifica este link para entender:

http://www.java2s.com/Code/Java/Network-Protocol/TransferafileviaSocket.htm

Outra coisa, como você está lendo o arquivo em blocos e não inteiramente, utilizou um laço while.

Se o seu arquivo for grande demais, o while poderá consumir processamento só para ele e dar a impressão que sua máquina travou.

Faz o seguinte, coloca um Thread.sleep(5) só para emprestar processamento para outras aplicações ou threads.

Muito obrigado pelas informações wiliamps, mas infelizmente a conexão não permanece após o envio do arquivo. E referente ao o while, não entendi qual seria a forma mais correta de envio.
Valeu pelas respostas!

Abraço!

W

Veja em seu Servidor que você obtem o in e out de um socket criado via construtor de Socket e não obtido pelo accept().

Ajustei seu código e rodei o algoritimo, a conexão não caiu, porém na segunda passagem do looping de envio de arquivo, o negócio trava.

Como você utilizou classes de alto nível que abstraem todo controle de socket, não saberia dizer a razão do travamento. Verifiquei todos os pontos e não vi nenhum motivo aparente.

Talvez seja o caso de você alterar o servidor e cliente para realizar uma nova conexão toda vez que concluir um ciclo para reiniciar a comunicação travada.

Sobre o while, quis chamar a atenção para o caso de um arquivo grande, pois como o envio é em blocos, isso poderia levar um tempão é seu programa consumirá CPU demais.

Para caso de looping demorados (seja while ou for), o melhor é você colocar dentro dele um intervalo para evitar consumo exclusivo de CPU.

while (true) {

   // Coloca no início do loop para evitar que não seja executado caso em algum lugar do loop você informe "continue;"
   try {
      Thread.sleep(10); // Dorme por 10 millisegundos
   } catch (InterruptedException e) {
      System.err.println("A thread foi interrompida.");
   }

   // Aqui são as instruções normais do looping

}

Teve um cara que usou o mesmo princípio utilizado por você para transferência de arquivos e teve problema também.

A sugestão foi mesmo refazer a conexão a cada envio.

http://www.coderanch.com/t/429978/sockets/java/Copying-Multiple-Files-Over-Socket

wiliamps

Criado 1 de outubro de 2012
Ultima resposta 4 de out. de 2012
Respostas 8
Participantes 3