Problema com socket

5 respostas
andersouza

Estou com um problema ao tentar criar um chat com socket.
Tudo estava dando certo até que inclui um HashMap para colocar o nome dos clientes e o OutputStream de cada um.
Mas quando crio cada cliente, é como se fosse criado um hashmap pra cada um,
e por consequencia as mensagens não são enviadas para todos.

Classe Servidor

public class Servidor {
	public static void main(String[] args) {
		// inicia o servidor
		new Servidor(12345).executa();
	}

	private int porta;
	private HashMap<String, PrintStream> mapaClientes;

	public Servidor(int porta) {
		this.porta = porta;
		this.mapaClientes = new HashMap<String, PrintStream>();
	}

	public void executa() {
		try {
			ServerSocket servidor = new ServerSocket(this.porta);
			System.out.println("Porta " + servidor.getLocalPort() + " aberta!");

			while (true) {
				// aceita um cliente
				Socket cliente = servidor.accept();

				// adiciona saida do cliente a lista
				PrintStream ps = new PrintStream(cliente.getOutputStream());
				// this.clientes.add(ps);

				// cria tratador de clientes numa nova thread
				TrataCliente tc = new TrataCliente(cliente.getInputStream(),
						ps, this);
				new Thread(tc).start();

				this.mapaClientes.put(tc.getNomeCliente(), ps);

				System.out.println(cliente.getLocalPort() + " "
						+ cliente.isConnected());
				System.out.println(this.mapaClientes.size());
			}
		} catch (IOException e) {
			System.out.println("IOException " + e);
		}
	}

	public void distribuiMensagem(String nome, String msg) {
		// envia msg para todo mundo
		for (int i = 0; i < this.mapaClientes.size(); i++) {
			PrintStream enviar = this.mapaClientes.get(i);
			enviar.println(nome + " diz: " + msg);
		}
	}
}

Classe que trata cada cliente

public class TrataCliente implements Runnable {

	private String nomeCliente;
	private InputStream entradacliente;
	private PrintStream saidaCliente;
	private Servidor servidor;
	
	public String getNomeCliente(){
		return this.nomeCliente;
	}

	public TrataCliente(InputStream entradacliente, PrintStream saidaCliente,
			Servidor servidor) {
		this.entradacliente = entradacliente;
		this.servidor = servidor;
		this.saidaCliente = saidaCliente;
	}

	public void run() {
		// pega o nome do cliente
		this.saidaCliente.println("Digite seu nome: ");
		Scanner s = new Scanner(this.entradacliente);
		this.nomeCliente = s.nextLine();

		// avisa ao cliente e ao servidor que a conexão foi feita
		System.out.println(this.nomeCliente + " se conectou!");
		this.saidaCliente.println("Bem-vindo " + this.nomeCliente);

		// quando chegar uma msg, distribui para todos
		while (s.hasNextLine()) {
			servidor.distribuiMensagem(this.nomeCliente, s.nextLine());
		}
		s.close();
		System.out.println(this.nomeCliente + " se desconectou!");		
	}
}

se alguem puder me dar uma luz do que pode estar errado…

Vlw

5 Respostas

A

Dá uma olhada na linha 33 de Servidor.

this.mapaClientes.put(tc.getNomeCliente(), ps);

Você está usando como chave do mapa o resultado de “tc.getNomeCliente()”. Neste ponto da execução você não tem garantia de que o “nomeCliente” é diferente de null, pois a Thread pode ainda não ter executado a llinha 23 da classe TrataCliente.

this.nomeCliente = s.nextLine();

Sendo assim, todo cliente que retornar null para o método getNomeCliente, na linha 33 de de Servidor, vai ser sobrescrito no mapaClientes, ficando apenas o último cliente inserido no mapa.

Outro problema é que como Servidor.mapaCliente é um HashMap o for da linha 46 de Servidor não vai funcionar. Aliás, nem compila. Já tentou compilar esta classe?

for (int i = 0; i < this.mapaClientes.size(); i++) {
PrintStream enviar = this.mapaClientes.get(i);

Só funcionaria se mapaCliente fosse um List (classe ArrayList, por exemplo).

andersouza

andreymb:
Dá uma olhada na linha 33 de Servidor.

this.mapaClientes.put(tc.getNomeCliente(), ps);

Você está usando como chave do mapa o resultado de “tc.getNomeCliente()”. Neste ponto da execução você não tem garantia de que o “nomeCliente” é diferente de null, pois a Thread pode ainda não ter executado a llinha 23 da classe TrataCliente.

this.nomeCliente = s.nextLine();

Sendo assim, todo cliente que retornar null para o método getNomeCliente, na linha 33 de de Servidor, vai ser sobrescrito no mapaClientes, ficando apenas o último cliente inserido no mapa.

Outro problema é que como Servidor.mapaCliente é um HashMap o for da linha 46 de Servidor não vai funcionar. Aliás, nem compila. Já tentou compilar esta classe?

for (int i = 0; i < this.mapaClientes.size(); i++) {
PrintStream enviar = this.mapaClientes.get(i);

Só funcionaria se mapaCliente fosse um List (classe ArrayList, por exemplo).

Eu resolveria se incluisse o nome do cliente dentro do TrataCliente logo após o nome ser inserido certo?

não vou conseguir testar agora porque meu eclipse ta zuado em casa, só consigo amanhã no trabalho, rsrs

quanto ao hashmap, compilou normal,
não vejo problema no for, qual seria?

aechiara

andersouza:
andreymb:
Dá uma olhada na linha 33 de Servidor.

this.mapaClientes.put(tc.getNomeCliente(), ps);

Você está usando como chave do mapa o resultado de “tc.getNomeCliente()”. Neste ponto da execução você não tem garantia de que o “nomeCliente” é diferente de null, pois a Thread pode ainda não ter executado a llinha 23 da classe TrataCliente.

this.nomeCliente = s.nextLine();

Sendo assim, todo cliente que retornar null para o método getNomeCliente, na linha 33 de de Servidor, vai ser sobrescrito no mapaClientes, ficando apenas o último cliente inserido no mapa.

Outro problema é que como Servidor.mapaCliente é um HashMap o for da linha 46 de Servidor não vai funcionar. Aliás, nem compila. Já tentou compilar esta classe?

for (int i = 0; i < this.mapaClientes.size(); i++) {
PrintStream enviar = this.mapaClientes.get(i);

Só funcionaria se mapaCliente fosse um List (classe ArrayList, por exemplo).

Eu resolveria se incluisse o nome do cliente dentro do TrataCliente logo após o nome ser inserido certo?

não vou conseguir testar agora porque meu eclipse ta zuado em casa, só consigo amanhã no trabalho, rsrs

quanto ao hashmap, compilou normal,
não vejo problema no for, qual seria?

do jeito que você fez o for ele tenta trazer os objetos com a chave para cada valor de i, ou seja de 0 até o tamanho do map, se você prestar atenção, provavelmente está vindo null, não está ? pois ele não encontra nenhum objeto com a chave que você está passando (o valor de i), você tem que iterarar pelos values ou pelos keys

algo assim:

Iterator iterator = mapaClientes.entrySet().iterator();
       
   while(iterator. hasNext())
   {        
      PrintStream ps = (PrintStream) iterator.next();
   }

aí você terá os PrintStream para enviar as mensagens

andersouza

Realmente eu estava usando o HashMap do jeito errado
mudei e agora estou usando um Map.Entry para poder enviar para todos.

E desde então mudei demais minhas classes:
Classe Servidor

public class Servidor {
	public static void main(String[] args) {
		// inicia o servidor
		new Servidor(12345).executa();
	}

	private int porta;
	private HashMap<String, PrintStream> mapaClientes;
	private ArrayList<String> nomes;

	public Servidor(int porta) {
		this.porta = porta;
		this.mapaClientes = new HashMap<String, PrintStream>();
		this.nomes = new ArrayList<String>();
	}

	public void executa() {
		try {
			ServerSocket servidor = new ServerSocket(this.porta);
			System.out.println("Porta " + servidor.getLocalPort() + " aberta!");

			while (true) {
				// aceita um cliente
				Socket cliente = servidor.accept();

				// adiciona saida do cliente a lista
				PrintStream ps = new PrintStream(cliente.getOutputStream());

				// cria tratador de clientes numa nova thread
				TrataCliente tc = new TrataCliente(cliente.getInputStream(),
						ps, this);
				new Thread(tc).start();
			}
		} catch (IOException e) {
			System.out.println("IOException " + e);
		}
	}

	public void mensagem(String nome, String msg) {
		// envia msg para todo mundo
		for (Map.Entry<String, PrintStream> clientes : mapaClientes.entrySet()) {
			String chave = clientes.getKey();
			PrintStream enviar = clientes.getValue();
			if (!chave.equals(nome)) {
				enviar.println(">>> " + nome + " diz: " + msg);
			}
		}
	}

	public void adicionarCliente(String nome, PrintStream cliente) {
		this.mapaClientes.put(nome, cliente);
		this.nomes.add(nome);
		System.out.println("Nova Conexão: " + nome);
		for (Map.Entry<String, PrintStream> clientes : mapaClientes.entrySet()) {
			String chave = clientes.getKey();
			PrintStream enviar = clientes.getValue();
			if (!chave.equals(nome)) {
				enviar.println(nome + " se conectou!");
				enviar.println("Conectados: " + this.nomes.toString());
			} else {
				enviar.println("Bem-vindo " + nome);
				enviar.println("Conectados: " + this.nomes.toString());
			}
		}
	}

	public void removerCliente(String nome) {
		this.mapaClientes.remove(nome);
		for (int i = 0; i < this.nomes.size(); i++) {
			if (this.nomes.get(i).equals(nome)) {
				this.nomes.remove(i);
			}
		}
		System.out.println("Conexão fechada: " + nome);
		for (Map.Entry<String, PrintStream> clientes : mapaClientes.entrySet()) {
			PrintStream enviar = clientes.getValue();
			enviar.println(nome + " saiu!");
			enviar.println("Conectados: " + this.nomes.toString());
		}
	}
}

Classe TrataCliente

public class TrataCliente implements Runnable {

	private String nomeCliente;
	private InputStream entradacliente;
	private PrintStream saidaCliente;
	private Servidor servidor;

	public TrataCliente(InputStream entradacliente, PrintStream saidaCliente,
			Servidor servidor) {
		this.entradacliente = entradacliente;
		this.servidor = servidor;
		this.saidaCliente = saidaCliente;
	}

	public void run() {
		// pega o nome do cliente
		this.saidaCliente.println("Digite seu nome: ");
		Scanner s = new Scanner(this.entradacliente);
		this.nomeCliente = s.nextLine();

		this.servidor.adicionarCliente(this.nomeCliente, this.saidaCliente);

		// quando chegar uma msg, distribui para todos
		while (s.hasNextLine()) {
			servidor.mensagem(this.nomeCliente, s.nextLine());
		}

		s.close();
		servidor.removerCliente(this.nomeCliente);
	}
}

Vlw pela ajuda
só mais uma dúvida,
estou tentado colocar um controle no servidor.
Tipo um menu para poder visualizar os clientes conectados, derrubar algum cliente, etc
Mas toda tentativa que eu faço para ler entradas no servidor acabam evitando que o resto do programa rode ou gera um erro.

Alguem teria uma idéia de como posso fazer isso?

andersouza

noguem sabe uma solução?

Criado 1 de março de 2012
Ultima resposta 8 de mar. de 2012
Respostas 5
Participantes 3