Pular para o conteúdo
24/08/2011 / viniciusknob

Chat Multicast UDP: uma tarefa de aula!

Em uma de minhas aulas de Desenvolvimento de Aplicações Distribuídas recebi a tarefa de criar um CHAT do tipo “Command Line” utilizando Multicast e UDP. Como saiu uma coisa legal então resolvi colocar o código a disposição, que claro pode ser melhorado. Foi utilizado Thread e apenas um arquivo foi criado. Para que exista comunicação, execute o arquivo em cada computador que se deseja efetuar a comunicação. A tarefa foi em grupo e os outros integrantes são citados no arquivo. A classe principal eu resolvi fazer uma referencia ao tipo de coleção utilizada para mensagens pendentes e ao próprio exercício, que ficou algo como My LinkedList Chat Multicast.

package br.com.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * Classe responsavel por cuidar da interacao do usuario. A classe obtem o nome
 * do usuario, mostra as mensagens pendentes recebedidas por uma thread e entao
 * pede se o usuario deseja digitar algo para enviar. A classe tambem instancia
 * a thread de controle do receive.
 *
 * @authors Vinicius Knob, Edipo Federle and Marcela Pedrini
 *
 */
public class MyLinkedListChatMulticast {

	/*
	 * Local de mensagens pendentes, ainda nao lidas. Todas as mensagens
	 * recebidas sao guardadas nesse local que eh um array ordenado para depois
	 * serem lidas. Esse array eh static pois precisa ser compartilhado com uma
	 * thread.
	 */
	private static LinkedList<String> mensagensPendentes = new LinkedList<String>();

	public static void main(String[] args) {
		int porta = 9999;
		InetAddress enderecoMulticast;
		MulticastSocket socket;
		String nomeUsuario;
		byte[] dados = new byte[140];
		DatagramPacket datagrama;

		try {

			/*
			 * Criando o chat
			 */
			enderecoMulticast = InetAddress.getByName("233.99.77.22");
			socket = new MulticastSocket(porta);
			socket.joinGroup(enderecoMulticast);

			/*
			 * Obtendo o nome do usuario do chat
			 */
			nomeUsuario = lerString("Digite seu nome: ");

			/*
			 * Sendo gentil com o usuario
			 */
			System.out.println("\n***** Ola " + nomeUsuario
					+ ", voce esta online! *****\n");

			/*
			 * Enviando apresentacao a todos do chat
			 */
			dados = criarStringConsole(nomeUsuario, "Estou Online!");
			datagrama = new DatagramPacket(dados, dados.length,
					enderecoMulticast, porta);
			socket.send(datagrama);

			/*
			 * Isso eh um bloco de limpesa do array de bytes.
			 */
			for (int i = 0; i < dados.length; i++) {
				dados[i] = 0;
			}

			/*
			 * Instanciando a thread para cuidar do recebimento de mensagens
			 */
			new GestorReceive(socket, datagrama, nomeUsuario).start();

			/*
			 * Loop que ira mostrar as mensagens pendentes e oferecer ao usuario
			 * para digitar algo. O boolean controla a vida do laco.
			 */
			boolean viver = true;
			while (viver) {

				/*
				 * Usuario tem a chance de digitar algo para enviar
				 */
				String mensagem = lerString(">> "); // bloqueante

				/*
				 * Quando o usuario nao deseja digitar nada, entao ele tera que
				 * digitar um codigo de escape "w!", se nao for usado esse
				 * codigo, eh por que o usuario digitou algo que deva ser
				 * enviado a todos.
				 */
				if (!mensagem.equals("w!")) {
					/*
					 * Se a mensagem for igual a "exit", significa que o usuario
					 * quer sair, entao a mensagem eh alterada para que seja
					 * enviado aos outros usuario algo mais decente = [USUARIO
					 * OFFLINE].
					 */
					if (mensagem.equals("exit")) {
						mensagem = "[USUARIO OFFLINE]";
						viver = false;
					}

					/*
					 * O que sera mostrado no console do usuario sera a hora
					 * atual, o nome do usuario que enviou a mensagem e a
					 * mensagem dele
					 */
					dados = criarStringConsole(nomeUsuario, mensagem);
					datagrama = new DatagramPacket(dados, dados.length,
							enderecoMulticast, porta);
					socket.send(datagrama);

					/*
					 * Isso eh um bloco de limpesa do array de bytes.
					 */
					for (int i = 0; i < dados.length; i++) {
						dados[i] = 0;
					}
				}
				/*
				 * Verifica a existencia de mensagens pendentes e as mostra
				 */
				verificarMensagensPendentes(nomeUsuario);

			}

		} catch (Exception exc) {
			throw new RuntimeException(exc.toString());

		}

	}

	/**
	 * Metodo que cria a string que deve ser mostrada para o usuario no console.
	 *
	 * @param nomeUsuario
	 *            - Nome do usuario.
	 * @param mensagem
	 *            - Mensagem enviada.
	 * @return Um array de bytes formatado.
	 */
	public static byte[] criarStringConsole(String nomeUsuario, String mensagem) {
		byte[] mensagemConsole = new byte[140];
		/*
		 * Montando uma string buffer para conter a mensagem
		 */
		StringBuffer stringMensagem = new StringBuffer();
		stringMensagem.append("[");
		stringMensagem.append(horaAtual());
		stringMensagem.append("] ");
		stringMensagem.append(nomeUsuario);
		stringMensagem.append(" diz: ");
		stringMensagem.append(mensagem);

		mensagemConsole = stringMensagem.toString().getBytes();

		return mensagemConsole;
	}

	/**
	 * Metodo para mostrar a hora atual no formato "HH:mm:ss"
	 *
	 * @return A hora atual formatada como HH:mm:ss
	 */
	public static String horaAtual() {
		return new SimpleDateFormat("HH:mm:ss").format(new Date());
	}

	/**
	 * Metodo para ler uma String do teclado. Retorna a String lida ou refaz o
	 * metodo caso houve algum erro na leitura.
	 */
	public static String lerString() {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		try {
			String lido = br.readLine();
			return lido;
		} catch (IOException e) {
			System.out.println("Erro ao ler a string! Digite novamente.");
			return lerString();
		}
	}

	/**
	 * Metodo que recebe e mostra uma mensagem, depois chama o metodo de ler uma
	 * nova String.
	 */
	public static String lerString(String mensagem) {
		System.out.print(mensagem);
		return lerString();
	}

	/**
	 * Metodo que verifica a existencia de mensagens pendentes. Esse metodo
	 * utiliza o metodo getMensagens() para esvaziar o array de mensagens caso
	 * exista algo. Nao retorna nada pois o que o metodo faz eh verificar as
	 * mensagens e ja mostra-las no console.
	 */
	public static synchronized void verificarMensagensPendentes(
			String nomeUsuario) {
		/*
		 * Buscando o array de mensagens pendentes
		 */
		LinkedList<String> mensagensParaVerificacao = new LinkedList<String>(
				getMensagens());

		/*
		 * Agui eh verificado se existem mensagens pendentes se existir, entao
		 * ele pegara uma a uma as mensagens ja ordenadas e mostrara no console
		 * para o usuario
		 */
		if (!mensagensParaVerificacao.isEmpty()) {
			Iterator<String> i = mensagensParaVerificacao.iterator();
			while (i.hasNext()) {
				String textoRecebido = i.next();

				/*
				 * Aqui eh verificado se na mensagem contem a palavra chave
				 * "EXIT", que indica que um usuario deseja encerrar o chat,
				 * tambem eh verificado se o usuario solicitante eh½ o mesmo que
				 * esta na maquina, para que nao exista conflito se a makina
				 * receber um exit de outro usuario.
				 */
				if ((textoRecebido.contains("[USUARIO OFFLINE]"))
						&& (textoRecebido.contains(nomeUsuario))) {
					System.exit(0);
				} else {
					System.out.println(textoRecebido.trim());
					System.out.flush();
				}
			}
		}
	}

	/**
	 * Metodo para pegar todas as mensagens pendentes. O metodo faz uma copia do
	 * array de mensagens e entao esvazia o array para que seja possivel receber
	 * novas mensagens sem existir as que ja foram lidas, sendo assim, cada vez
	 * que as mensagens forem pegas, nao serao mostradas as mensagens ja pegas
	 * anteriormente.
	 *
	 * @return Um array de todas as mensagens recebidas.
	 */
	public static LinkedList<String> getMensagens() {
		LinkedList<String> copiaMensagensPendentes = new LinkedList<String>(
				mensagensPendentes);
		mensagensPendentes.clear();
		return copiaMensagensPendentes;
	}

	/**
	 * Metodo para substituir o array existente por um novo array vazio para
	 * receber mensagens
	 *
	 * @param mensagens
	 *            - O novo array de mensagens.
	 */
	public static void setMensagens(LinkedList<String> mensagens) {
		mensagensPendentes = new LinkedList<String>(mensagens);
	}

	/**
	 * Metodo para armazenar uma mensagem como pendente no array de mensagens.
	 *
	 * @param mensagem
	 *            - A mensagem para ser armazenada.
	 */
	public static synchronized void setMensagem(String mensagem) {
		mensagensPendentes.add(mensagem);
	}

}

/**
 * Thread que ficara encarregada de controlar o recebimento de mensagens por
 * receive. Essa Thread guarda as mensagens em um array que seria um local para
 * mensagens pendentes, ainda nao lidas.
 *
 * @authors Vinicius Knob, Edipo Federle and Marcela Pedrini
 *
 */
class GestorReceive extends Thread {
	MulticastSocket socket;
	String nomeUsuario;
	byte[] dados = new byte[140];
	DatagramPacket datagrama;

	public GestorReceive(MulticastSocket skt, DatagramPacket dtg, String nmusr) {
		socket = skt;
		datagrama = dtg;
		nomeUsuario = nmusr;
	}

	public void run() {

		try {

			/*
			 * Essa variavel vai controlar a vida da thread
			 */
			boolean viver = true;

			/*
			 * Loop encarregado de receber mensagens e guarda-las no array de
			 * mensagens pendentes
			 */
			while (viver) {

				/*
				 * Colocando um array de dados vazio sobre o array anterior
				 */
				datagrama.setData(dados);
				/*
				 * Receive eh bloqueante
				 */
				socket.receive(datagrama);
				dados = datagrama.getData();
				String mensagem = new String(dados);

				/*
				 * Aqui eh verificado se na mensagem contem a palavra chave
				 * "EXIT", que indica que um usuario deseja encerrar o chat,
				 * tambem eh verificado se o usuario solicitante eh o mesmo que
				 * esta na maquina, para que nao exista conflito se o makina
				 * receber um exit de outro usuario. Isso encerrara a vida da
				 * thread.
				 */
				if ((mensagem.contains("exit"))
						&& (mensagem.contains(nomeUsuario))) {
					viver = false;
				}
				MyLinkedListChatMulticast.setMensagem(mensagem);

				/*
				 * Isso eh um bloco de limpesa do array de bytes.
				 */
				for (int i = 0; i < dados.length; i++) {
					dados[i] = 0;
				}
			}

		} catch (Exception exc) {
			System.err.println(exc.toString());
		}
	}
}

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.