[Resolvido] Problema com o exemplo de chat do livro do Deitel

8 respostas
MakinoJapa

Caros,

estou tentando implementar um chat, seguindo o exemplo apresentado no livro.

Bem, consegui me conectar ao ServerMessenger certinho...

Quando eu envio a primeira mensagem... dá tudo certo...

A mensagem chega no servidor e é distribuída para todos os clientes....

Só que quando envio a segunda mensagem ou o outro cliente vai responder, acontece o seguinte:

No server dá o seguinte erro:

[color=red]
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.BufferededReader.fill(Unknown Source)
at java.io BufferededReader.readLine(Unknown Source)
at java.io BufferededReader.readLine(Unknown Source)
at chat.ReceivingThread.run(ReceivingThread.java:51)
[/color]

A minha classe ReceivingThread é essa:

package chat;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.InterruptedIOException;
    import java.net.Socket;
    import java.util.StringTokenizer;

    // Thread que espera mensagens de um cliente 
    // particular e envia mensagens para um MessageListener;
    // espera novas mensagens de clientes do MessengerServer em Threads separadas
    public class ReceivingThread extends Thread implements SocketMessengerConstants{
	
            private BufferedReader input;
	    private MessageListener messageListener;
	    private boolean keepListening = true;
	
	    // construtor
	    public ReceivingThread(MessageListener listener, Socket socketCliente) {
		
		    // invoca o construtor da super classe para dar nome à Thread
		    super("ReceivingThread: " + socketCliente);
		
		    // configura o ouvinte para o qual a novas mensagens devem ser enviadas
		    messageListener = listener;
		
		    // configura o tempo máximo de espera para leitura do socketCliente
		    // e cria um BufferedReader para ler as mensagens que chegam
		    try {
			    socketCliente.setSoTimeout(30000);
			
			    input = new BufferedReader(new InputStreamReader(socketCliente.getInputStream()));
			
		    } catch (IOException e) {
			
			    e.printStackTrace();
		    }
	    }
	
	    // espera novas mensagens e as envia ao MessageListener
	    public void run(){
		
		    String message;
		
		    // espera novas mensagens até que seja parado
		    while (keepListening){
			
			    // lê mensagem do BufferedReader
			    try{
				    message = input.readLine();
			
				    // trata exceção quando o tempo máximo para leitura é esgotado
			    }catch (InterruptedIOException interruptedIOException){				
				    continue;
			
			    }catch (IOException e){
				    e.printStackTrace();
				    break;
			    }
			
			    // assegura que a mensagem não está vazia
			    if(message != null){
				
				    // separa a mensagem em tokens para recuperar 
				    //o nome do usuário e o corpo da mensagem
				    StringTokenizer stringTokenizer = new StringTokenizer(message, MESSAGE_SEPARATOR);
				
				    // ignora mensagens que não têm um nome de usuário e um corpo de mensagem
				    if(stringTokenizer.countTokens() == 2){
					
					    // envia mensagem para o MessageListener
					    messageListener.messageReceived(
							    stringTokenizer.nextToken(),   // nome do usuario
							    stringTokenizer.nextToken()); // corpo da mensagem
				    }
				    else 
					
					    // se recebeu mensagem de desconexão, para de esperar mensagens
					    if(message.equalsIgnoreCase(MESSAGE_SEPARATOR + DISCONNECT_STRING)){
						    stopListening();
					    }
			    }
			
		    }
		
		    // fecha o BufferedReader (também fecha o socket)
		    try{
			    input.close();
		    }catch (IOException e){
			    e.printStackTrace();
		    }
	    }

	    // para de esperar por mensagens
	    public void stopListening(){
		    keepListening = false;
	    }
    }

E no cliente dá o seguinte erro:

[color=red]java.net.SocketException: Socket closed
at java.net.PlainDatagramSocketImpl.receive0(Native Method)
at java.net.PlainDatagramSocketImpl.receive(Unknown Source)
at java.net.DatagramSocket.receive(Unknown Source)
at chat.PacketReceivingThread.run(PacketReceivingThread.java:62)[/color]

A minha classe PacketReceivingThread é a seguinte:

package chat;

    import java.io.IOException;
    import java.io.InterruptedIOException;
    import java.net.DatagramPacket;
    import java.net.InetAddress;
    import java.net.MulticastSocket;
    import java.util.StringTokenizer;

    public class PacketReceivingThread extends Thread implements SocketMessengerConstants{

	    // MessageListener ao qual as mensagens devem ser entregues
	    private MessageListener messageListener;
	
	    // Multicatsocket para receber as mensagens transmitidas
	    private MulticastSocket multicastSocket;
	
	    // InetAddress do grupo de mensagens
	    private InetAddress multicastGrupo;
	
	    // indicador para terminar a PacketReceivingThread
	    private boolean keepListening = true;
	
	    // construtor
	    public PacketReceivingThread(MessageListener listener) {
		
		    super("PacketReceivingThread");
		
		    messageListener = listener;
	
		    // conecta o MulticastSocket ao endereco e a porta de multicast
		    try{
			    multicastSocket = new MulticastSocket(MULTICAST_LISTENING_PORT);
			
			    multicastGrupo = InetAddress.getByName(MULTICAST_ADDRESS);
			
			    // junta-se ao grupo de multicast para receber mensagens
			    multicastSocket.joinGroup(multicastGrupo);
			
			    // ajusta tempo máximo de 5 segundos para esperar novos pacotes
			    multicastSocket.setSoTimeout(20000);
		
		    }catch (IOException e) {
			    e.printStackTrace();
		    }
	    }
	
	    // espera as mensagens do grupo de multicast
	    public void run(){
		
		    // espera as mensagens até ser parada
		    while(keepListening){
			
			    // cria buffer para mensagens que chegam
			    byte[] buffer = new byte[MESSAGE_SIZE];
		
			    // cria DatagramPacket para mensagen que chega
			    DatagramPacket datagramPacket = new DatagramPacket(buffer, MESSAGE_SIZE);
			
			    // recebe novo DatagramPacket (chamada bloqueante)
			    try{
				    multicastSocket.receive(datagramPacket);
			
			    }catch (InterruptedIOException interruptedIOException){
				    interruptedIOException.printStackTrace();
			
			    }catch (IOException e){
				    e.printStackTrace();
				    break;
			    }
			
			    // coloca dados da mensagem em uma String
			    String message = new String(datagramPacket.getData());
			
			    // assegura que a mensagem não está vazia
			    if(message != null){
				
				    // elimina espaço em branco extra no fim da mensagem
				    message = message.trim();
				
				    // separa a mensagem em tokens para separar nome do usuario do corpo da mensagem
				    StringTokenizer stringTokenizer = new StringTokenizer(message, MESSAGE_SEPARATOR);
				
				    // ignora mensagens que não têm nome de usuario e corpo
				    if(stringTokenizer.countTokens() == 2){
					
					    // envia a mensagem para o MessageListener
					    messageListener.messageReceived(stringTokenizer.nextToken(), stringTokenizer.nextToken());
				    }
			    }
			
			    // sai do grupo de multicast e fecha o MulticastSocket
			    try{
				    multicastSocket.leaveGroup(multicastGrupo);
				    multicastSocket.close();
			
			    }catch (IOException e){
				    e.printStackTrace();
			    }
		    }
		
	    }
	
	    // para de esperar por novas mensagens
	    public void stopListening(){
		    keepListening = false;
	    }

    }

Depurando eu percebi que na classe ReceivingThread do Servidor...
A execução fica muito tempo na linha 51

message = input.readLine();

Até cair na Exception InterruptedIOException

Alguém sabe me dizer o porquê?

Obrigada!

8 Respostas

Marcio_Duran

Olá amigo, fica complicado você expor exercicios do livro do Deitel em um Forúm de Java onde as pessoas no minimo vem aqui já trocar informações ou mesmo citar partes de codigos onde a interpretação já advem de um profissional mais informado, claro que não deixa de ser válida.

Entretando existe uma comunidade JavaLivros onde pessoa que tem o mesmo livro que você comprou já testou o codigo e já leu também a obra, eu recomendo você a frequentar o JavaLivros e levar essa informação para lá também, eu vou verificar o codigo mas eu não tenho o livro do Deitel pra lhe acompanhar…

[size=18][color=orange]Comunidade JavaLivros[/color] [/size]
:arrow: http://javalivros.ning.com/

Abraçoss…

B

Vejo duas classes que recebem os dados, mas cadê a que manda?

MakinoJapa

Ok.

Do lado cliente:

package chat;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

// SendingThread para entregar mensagens enviadas ao servidor
public class SendingThread extends Thread implements SocketMessengerConstants{
	
	// socket pelo qual a mensagem deve ser enviada
	private Socket socketCliente;
	private String messageToSend;
	
	// construtor
	public SendingThread(Socket socket, String userName, String message) {
		
		super("SendingThread:" + socket);
		
		socketCliente = socket;
		
		// constroi a mensagem a ser enviada
		messageToSend = userName + MESSAGE_SEPARATOR + message;
	}
	
	// envia a mensagem e sai da thread
	public void run(){
		
		// envia a mensagem e descarrega PrintWriter
		try{
			PrintWriter printWriter = new PrintWriter(socketCliente.getOutputStream());
			printWriter.println(messageToSend);
			printWriter.flush();
		
		}catch (IOException e){
			e.printStackTrace();
		}
	}
}

Do lado servidor:

package chat;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

// entrega mensagens enviadas para um grupo de multicast através de DatagramPackets
public class MulticastSendingThread extends Thread implements SocketMessengerConstants{
	
	// dados da mensagem
	private byte[] messageBytes;
	
	// construtor
	public MulticastSendingThread(byte[] bytes) {
		
		// invoca o construtor da superclasse para nomear a Thread
		super("MulticastSendingThread");
		
		messageBytes = bytes;
		
	}
	
	// entrega a mensagem ao MULTICAST_ADDRESS através do DatagramSocket
	public void run(){
		
		// entrega a mensagem
		try{
			
			// cria o DatagramSocket para enviar a mensagem
			DatagramSocket datagramSocket = new DatagramSocket(MULTICAST_SENDING_PORT);
			
			// usa o InetAddress reservado para o grupo de Multicast
			InetAddress grupo = InetAddress.getByName(MULTICAST_ADDRESS);
			
			// cria o DatagramPacket que contém a mensagem
			DatagramPacket datagramPacket = new DatagramPacket(messageBytes, messageBytes.length,
					grupo, MULTICAST_LISTENING_PORT);
			
			// envia o pacote para um grupo de multicast e fecha o socket
			datagramSocket.send(datagramPacket);
			datagramSocket.close();
			
		}catch (IOException e){
			e.printStackTrace();
		}
	}
	

}
B

Fora o cliente não usar o método close no PrintWriter, não detectei nada de errado, olhando por cima.

MakinoJapa

Bem, adicionei o método close ao Printwriter e quando eu vou enviar a segunda mensagem,
dá esse erro aqui:

[color=red]java.net.SocketException: Socket is closed
at java.net.Socket.getOutputStream(Unknown Source)
at chat.SendingThread.run(SendingThread.java:30)[/color]

Não acredito que seja isso… parece que acontece alguma coisa no servidor…

B

O que estou vendo é que o servidor sempre fecha a conexão quando recebe a mensagem, porem o Cliente sempre acha que a conexão está aberta do socket.

Bem, você tem que fazer eles entrarem num acordo.

Eu recomendo você implementar o Cliente para que ele veja se a conexão está aberta antes mandar a mensagem. Se não estiver, que ele requisite uma conexão com o servidor da classe que controla isso.

MakinoJapa

Bruno, era isso mesmo... o servidor estava fechando a conexão a toda hora...

Fui repassando pelas classes para ver por que isso acontecia e adivinha só...

Na classe PacketReceivingThread sem querer eu coloquei o bloco try catch

que sai do grupo de multicast e fecha o MulticastSocket dentro do while(keepListening):

// espera as mensagens até ser parada
		    while(keepListening){
			
			    // cria buffer para mensagens que chegam
			    byte[] buffer = new byte[MESSAGE_SIZE];
		
			    // cria DatagramPacket para mensagen que chega
			    DatagramPacket datagramPacket = new DatagramPacket(buffer, MESSAGE_SIZE);
			
			    // recebe novo DatagramPacket (chamada bloqueante)
			    try{
				    multicastSocket.receive(datagramPacket);
			
			    }catch (InterruptedIOException interruptedIOException){
				    interruptedIOException.printStackTrace();
			
			    }catch (IOException e){
				    e.printStackTrace();
				    break;
			    }
			
			    // coloca dados da mensagem em uma String
			    String message = new String(datagramPacket.getData());
			
			    // assegura que a mensagem não está vazia
			    if(message != null){
				
				    // elimina espaço em branco extra no fim da mensagem
				    message = message.trim();
				
				    // separa a mensagem em tokens para separar nome do usuario do corpo da mensagem
				    StringTokenizer stringTokenizer = new StringTokenizer(message, MESSAGE_SEPARATOR);
				
				    // ignora mensagens que não têm nome de usuario e corpo
				    if(stringTokenizer.countTokens() == 2){
					
					    // envia a mensagem para o MessageListener
					    messageListener.messageReceived(stringTokenizer.nextToken(), stringTokenizer.nextToken());
				    }
			    }
			
			    // sai do grupo de multicast e fecha o MulticastSocket
			    try{
				    multicastSocket.leaveGroup(multicastGrupo);
				    multicastSocket.close();
			
			    }catch (IOException e){
				    e.printStackTrace();
			    }
		    }

Por isso que após a primeira mensagem o socket já era fechado.

Foi só tirar de dentro do while que funcionou... rs... :D

Ai,ai! Que newba! ><'
:oops:

Vlw pela ajuda, Bruno! :wink:

Tuanny_Ramos

ola, gostaria de saber o que virou o projeto
pois estou fazendo também e estou com problemas

Criado 12 de abril de 2009
Ultima resposta 23 de mar. de 2010
Respostas 8
Participantes 4