[Resolvido] Sockets - Saber se uma ponta fechou

17 respostas
R

Boa noite pessoal,

Estou estudando um pouco sobre sockets no Java.

Acredito estar indo bem, fiz um lab de um joguinho da velha só pra aplicar o aprendizado, e está funcionando como deveria.

A única dificuldade que estou tendo, é em saber se um socket continua ativo no cliente.

Com o ServerSocket eu fico gerando novos sockets para as requisições que chegam, porém, se um cliente fechar essa conexão eu não estou conseguindo saber (identificar no server que o client fechou aquela conexão).

Estou usando um loop em cima do socket.isConnected(), porém ele sempre retorna true, mesmo se no client eu der um socket.close().

Alguém ja mexeu com isso e saberia me dizer o que pode estar errado? Toda ajuda é muito bem vinda!

Muito obrigado,

Rodrigo

17 Respostas

Y

Opa cara blza?

Pode postar o código pra nós?

Em todos os casos creio que você pode controlar se um socket fechou ou não dentro do loop no momento que você aceitar uma conexão do ServerSocket.

Feito isso na hora em que você manipular o socket do cliente se o mesmo estiver fechado vai ser lançado uma excessão.
Eu não sei qual é a sua condição no while. Se for um ServerSocket.isConnected() está errado pois este é o socket do servidor e não do cliente que você pegou pelo método accept.

Por isso seria importante ver o código.

exemplo:

ServerSocket conexServer = new ServerSocket(porta);

while(true){
 try{
       Socket cliente = conexServer.accept();
       DataOutputStream saida = new DataOutputStream(cliente.getOutputStream);
       saida.readUTF("Alguem ai?");
    }catch(SocketException ex){
         //Socket esta fechado. Fazer alguma coisa nessa excessão.
    }catch(IOException ex){
    }
}

Se alguém tem uma maneira mais elegante de efetuar isso me corrija por favor. Obrigado.

Espero ter ajudado!

att,
yuri

R

Boa tarde Yuri,

Cara os códigos estão no meu notebook, esse fim de semana eu posto aqui pra você dar uma olhada.

Essa forma que você comentou, de usar um loop pra ficar sempre aceitando requisições, eu já faço e funciona bem.

O que eu gostaria é de saber no server quando um client fecha (socket.close()) uma conexão.

Até o momento não consegui fazer da forma que eu queria, mas estou fazendo o seguinte:

O client antes de fechar a conexão avisa o server que vai fechar, o server quando fica sabendo também fecha, com isso eu consigo o fechamento completo do socket.

Mas não creio ser a forma correta, ao meu ver o socket do lado server deveria saber “sozinho” quando o lado client fechou.

Abraços!

E

Você também escreveu o lado que fecha o socket ? Experimente, antes de fechar o socket , usar shutdownInput e shutdownOutput.

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

Y

Opa blza?

Mas então é bem aqui que vc trata isso. Pois é quando você aceita um socket e o servidor envia alguma coisa pro cliente, se o mesmo estiver fechado vai lançar uma Excessão: SocketException.

Voce pode tratar no catch mesmo :slight_smile:

abraço!

R

entanglement:
Você também escreveu o lado que fecha o socket ? Experimente, antes de fechar o socket , usar shutdownInput e shutdownOutput.

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

Cara eu cheguei a ver isso anteriormente, e ai o loop eu fazia em cima do !socket.isInputShutdown(), mas não funcionou.

A impressão que da, é que ele considera somente o lado em que você está trabalhando, mas nunca o lado oposto. Por exemplo, se no client eu dou um socket.shutdownInput(), o server não fica sabendo disso.

Será que é pra ser assim mesmo, e meu entendimento está errado?

Abraços!

R

ykilian2006:
Opa blza?

Mas então é bem aqui que vc trata isso. Pois é quando você aceita um socket e o servidor envia alguma coisa pro cliente, se o mesmo estiver fechado vai lançar uma Excessão: SocketException.

Voce pode tratar no catch mesmo :slight_smile:

abraço!

Isso só acontece quando eu fecho o socket do lado em que eu for testar.

Por exemplo, se eu fechar o socket lá do lado client, e no lado server dar um while (!socket.isClosed()), ele continua loopando, até eu dar um close no socket do lado server.

Como eu havia prometido, abaixo seguem os trechos do meu código que controlam a parte dos sockets:

Socket “factory” (lado server)

serverSocket = new ServerSocket(port);

	while (!serverSocket.isClosed()){
				
		Socket socket = serverSocket.accept();
				
		SocketAdmin socketAdmin = new SocketAdmin(socket); // É uma thread
		socketAdmin.start();

	}

SocketAdmin

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

	while (!socket.isClosed()){
		//...
	}

Socket Client

socket = new Socket(host, port);
			
	in = new DataInputStream(socket.getInputStream());
	out = new DataOutputStream(socket.getOutputStream());
			
	while (!socket.isClosed()) {
		//...
	}

Pelo meu entendimento, se eu desse um socket.close() no client, o server (meu SocketAdmin) deveria parar o loop.

Mas sei lá, to começando a achar que meu entendimento que está errado, e que essa minha "crença"está errada rs

Abraços!

V

O problema é que sem uma mensagem de “socket fechado” pode demorar muito até o servidor perceber que o socket fechou.
Inclua uma mensagem de “CAINDO_FORA” no seu cliente, e faça o servidor fechar quando recebe-la (os métodos que o entaglement sugeriu deveriam fazer isso).

É bom também incluir mensagens de KEEP_ALIVE de tempos em tempos. Digamos, se o servidor ficar mais do que 30 segundos sem receber uma mensagem do cliente, ele envia um KEEP_ALIVE e espera por mais 5 segundos. Se uma mensagem de ALIVE não vier na resposta, o servidor fecha aquela conexão. O mesmo pode ser feito no lado do cliente.

R

ViniGodoy:
O problema é que sem uma mensagem de “socket fechado” pode demorar muito até o servidor perceber que o socket fechou.
Inclua uma mensagem de “CAINDO_FORA” no seu cliente, e faça o servidor fechar quando recebe-la (os métodos que o entaglement sugeriu deveriam fazer isso).

É bom também incluir mensagens de KEEP_ALIVE de tempos em tempos. Digamos, se o servidor ficar mais do que 30 segundos sem receber uma mensagem do cliente, ele envia um KEEP_ALIVE e espera por mais 5 segundos. Se uma mensagem de ALIVE não vier na resposta, o servidor fecha aquela conexão. O mesmo pode ser feito no lado do cliente.

Fala ViniGodoy!

Então, o que estou fazendo é exatamente isso, eu aviso o server que vou fechar com uma mensagem, e fecho o client. Quando o server recebe aquele aviso, ele fecha também. Com isso estou conseguindo um log de quando o client fecha.

Porém é meio falho, pois o client pode fechar de forma inesperada (erro no SO por exemplo) e nunca avisar o server.

Agora, essa sua sugestão do kEEP_ALIVE eu não testei. Vou testar ela, e também TIMEOUT.

Depois eu posto aqui o resultado.

Muito obrigado!

V

O timeout do TCP é sempre extremamente longo. Por isso a conexão fica aberta por muito tempo.
Acho que a única maneira confiável de fazer esse fechamento é mesmo através de mensagens KEEP_ALIVE <-> ALIVE.

Eu também prefiro ao tentar configurar o timeout do SO, pois esse timeout nem sempre é configurável. Com o keep alive, você independente do usuário ter permissão, ou mesmo do SO dar suporte a essa configuração.

R

ViniGodoy:
O timeout do TCP é sempre extremamente longo. Por isso a conexão fica aberta por muito tempo.
Acho que a única maneira confiável de fazer esse fechamento é mesmo através de mensagens KEEP_ALIVE <-> ALIVE.

Eu também prefiro ao tentar configurar o timeout do SO, pois esse timeout nem sempre é configurável. Com o keep alive, você independente do usuário ter permissão, ou mesmo do SO dar suporte a essa configuração.

É cara, no meu caso nem mesmo o keepAlive funcionou, esperei bastante tempo após a ponta client ter sido fechada, porém a ponta server nunca soube disso.

Será que pode ser alguma coisa relacionada a SO? No meu caso é o Mac OS X Lion.

Abraços!

V

Impossivel. Keep alive/alive são mensagens do seu protocolo. Você as implementou?

Não há métodos prontos no java para elas.

R

ViniGodoy:
Impossivel. Keep alive/alive são mensagens do seu protocolo. Você as implementou?

Não há métodos prontos no java para elas.

Ah, não é só setar o setKeepAlive(true)?

Putz cara então comi bola, achei que fosse só isso, tava até pensando em implementar um keepAlive meu rs

V

Não. Esse já e true por padrao. Mas o keep alive do tcp e muito lento. Tem que fazer o seu mesmo, rs.

A

a única forma confiável de saber se a conexão está ativa ou não é enviando algum dado, no caso pode ser o keep alive implementado por vc como já foi sugerido, eu costumo enviar 1 byte pré-determinado, e no cliente quando ele recebe esse 1 byte, ele apenas responde com outro byte, ou o mesmo, aí é vc quem decide, se ocorrer algum erro quando o servidor tentar escrever no socket, a conexão caiu por algum motivo qualquer e vc pode finalizá-la no servidor

no cliente é mais fácil, pois se a conexão cair, vc tenta reconectar, e está lidando apenas com 1 conexão
no servidor é um pouco mais difícil, vai consumir um pouco mais de recursos, mas seria interessante se vc conseguisse manter uma lista de todas as conexões ativas, e enviar a sua mensagem de keep alive para todas, e quem não responder, no servidor vc da close naquele socket / thread …

R

aechiara:
a única forma confiável de saber se a conexão está ativa ou não é enviando algum dado, no caso pode ser o keep alive implementado por vc como já foi sugerido, eu costumo enviar 1 byte pré-determinado, e no cliente quando ele recebe esse 1 byte, ele apenas responde com outro byte, ou o mesmo, aí é vc quem decide, se ocorrer algum erro quando o servidor tentar escrever no socket, a conexão caiu por algum motivo qualquer e vc pode finalizá-la no servidor

no cliente é mais fácil, pois se a conexão cair, vc tenta reconectar, e está lidando apenas com 1 conexão
no servidor é um pouco mais difícil, vai consumir um pouco mais de recursos, mas seria interessante se vc conseguisse manter uma lista de todas as conexões ativas, e enviar a sua mensagem de keep alive para todas, e quem não responder, no servidor vc da close naquele socket / thread …

Entendi cara, é vou fazer esse meu keepAlive…ainda não tive tempo, como é um projetinho pessoal só consigo fazer de casa, vamos ver se hoje da tempo de fazer a noite.

Depois de testar, coloco no título do posto um “RESOLVIDO” pra ajudar quem tiver essa dúvida também.

To gostando de mexer com sockets, queria fazer de laboratório um jogo online, uma espécie de Tibia, só pra exercitar.

Muito obrigado!

R

Pessoal,

Consegui fazer o teste de keepAlive.

Agora sim está funcionando corretamente, em ambas as pontas eu fico mandando um “socket.getOutputStream().write(1);”, e quando um das pontas fechou, recebo a exception “Connection reset by peer: socket write error”.

Muito obrigado a todos que ajudaram!

ViniGodoy, dei uma olhada no Ponto V, gostei bastante. Conforme eu havia comentado, quero fazer um jogo online no estilo Tibia, para desenvolvier o aprendizado, creio que vou usar muito o site como referência!

Abraços!

R

Amigos,

Me surgiu uma outra dúvida, não sei se devo perguntar neste post mesmo ou seria melhor abrir um novo.

A questão é que no código a seguir, ele trava no readUTF().

if (in.available() > 0){
	String tmp = in.readUTF();
	//faço alguma coisa com o tmp
}

Ele segura a thread nele, só “destrava” quando eu dou um close() nesse socket.

Alguém sabe me dizer porque?

Muito obrigado!


Editado:

Acho que era algum erro meu, pois recomecei o código do zero e agora está ok.

Valeu!

Criado 4 de abril de 2012
Ultima resposta 11 de abr. de 2012
Respostas 17
Participantes 5