Escolar Documentos
Profissional Documentos
Cultura Documentos
Relatorio Projeto Entrega2
Relatorio Projeto Entrega2
2ª ENTREGA
Relatório do Projeto
Guilherme José de Sousa Inês Capa de Barros Maria Beatriz Garcez Morais
Barbosa
1. Implementação:
PresencesServer
Começamos por pedir a porta na qual o servidor deseja operar para que qualquer conexão seja
estabelecida. Pedimos também que seja indicado um valor para o parâmetro SESSION_TIMEOUT,
inicializamos as Presences passando como parâmetro o valor inserido e inicializamos o servidor com a porta
que ele previamente selecionou.
O servidor está sempre aberto a aceitar ligações vindas de clientes. Cada vez que temos uma nova
ligação de um cliente ao servidor, criamos e adicionamos uma instância do atendePedidoTCP (passamos
como parâmetro a ligação socket e o Servidor que lhe deu origem) à lista de utilizadores.
De modo que o servidor esteja atualizado e atualize os clientes em relação ao número de
conectados, vamos ter um temporizador que a cada segundo compara os utilizadores que estão ativos
com os utilizadores que estão presentes na lista static que contém todos os objetos do tipo
atendePedidoTCP.
Percorremos a lista static e por cada utilizador que estiver nessa lista, mas não estiver na dos
ativos, adicionamos à lista dos utilizadores inativos.
Por fim, para cada elemento da lista dos inativos enviamos um “SESSION_TIME_OUT”, fechamos
a ligação com o servidor e removemos da lista static de utilizadores.
AtendePedidoTCP
Esta classe gere a ligação de cada cliente com o servidor e é instanciada no PresencesServer cada
vez que uma nova ligação é efetuada.
Quando o cliente envia um pedido, este está contido numa só string que contem o tipo de pedido e o
respetivo conteúdo separada por “:”, que é lida pelo atendePedidoTCP o qual os vai separar. Consoante o
cabeçalho da mensagem que pode ser "SESSION_UPDATE_REQUEST" ou "AGENT_POST" o servidor terá
um comportamento diferente:
• Se for do tipo "AGENT_POST", significa que foi uma mensagem que o cliente enviou e vai guardá-
la nas 10 mensagens mais recentes.
• Se for do tipo "SESSION_UPDATE_REQUEST" e o AtendePedidoTCP não tiver nickname, ou seja,
se ainda não se tiver conectado, então este passa a ser igual ao nickname enviado no pedido do
cliente e este é adicionado à lista de utilizadores.
Esta classe serve para guardar a informação relativa ao cliente: IP, nickname, regista quando foi a
última vez que este efetuou um pedido ao servidor e regista também se este deseja comunicar
remotamente(RMI) . O método TimeOutPassed() consiste em verificar a diferença do tempo atual e da
última vez que o cliente foi visto. Se este tempo for maior do que o tempo estipulado para o
“TIME_OUT”, então o cliente será visto como inativo.
Presences
Criamos uma hashtable para incluir os dados dos clientes, sendo a chave o IP e para cada chave
tem associado um objeto da classe IPInfo. Estes dados contém a informação relativa aos IP’s, os
respetivos nicknames, a data mais recente na qual o cliente foi visto, ou seja, a última vez que efetuou
um pedido ao servidor e também se este deseja comunicar remotamente(RMI).
No método getNickNameList() começamos por percorrer a lista de objetos IPInfo e para cada
elemento vamos verificar se dentro do tempo estipulado como tempo de “TIME_OUT” o cliente realizou
algum pedido, se não tiver realizado não é adicionado novamente à lista, caso contrário o nome dele é
adicionado novamente e o cronometro é reiniciado.
ConfiguracoesCliente
Esta interface será usada com o intuito de receber informações sobre como será estabelecida a
conexão e com quem. Para isso pedimos ao cliente que insira o endereço ao qual se pretende conectar, a
porta do respetivo endereço e, se este pretender comunicar remotamente(RMI), deve selecionar a
checkbox. No caso do cliente não inserir nem o endereço nem a porta aos quais se deseja conectar, será
utilizada a informação default. E se não selecionar a checkbox, o cliente não irá comunicar remotamente
com outros. De seguida inicializamos a interface cliente.
InterfaceCliente
Começamos por inicializar a interface do cliente. Decidimos o modelo para cada uma das tabelas
e criamos o painel que representa toda a nossa interface. É nesta interface que o cliente se pode conectar
com o servidor, para isso criamos um botão “Conectar” que estabelece a ligação com o servidor através do
método iniciar(). Este método cria o Socket, o canal de comunicação com o servidor, que serve para entrada
(BufferedReader) e saída (PrintWriter) de dados. O cliente deve inserir um nickname válido, ou seja, que
não seja nulo e, posteriormente, iniciamos o método iniciarLoop() que nos irá permitirá realizar um
"SESSION_UPDATE_REQUEST” a cada 120 segundos.
Para recebermos as mensagens que o servidor envia, criamos uma thread que está sempre em
execução enquanto houver ligação entre o cliente e o servidor. Esta thread é responsável por ver se a
mensagem recebida é do tipo "SESSION_UPDATE”. Caso seja, prepara o cliente para este receber a lista
dos utilizadores presentes e as 10 mensagens mais recentes. A ordem pela qual é recebida a informação é
a mesma pela qual é enviada pelo servidor.
No método criarRegisto() criamos o registo RMI na porta 1099 e criamos um objeto remoto. Para
esse registo, em localhost, precisa de criar um serviço com o nome “PrivateMessaging” que depois é
associado ao objeto remoto.
No método separarNicknameIpRmi (int linha, int coluna) vamos buscar a linha selecionada da
tabela das Presenças e separamos o nickname do Ip da informação relativa ao RMI(true/false). Fazemos
verificações não permitindo que o cliente envie mensagens para si próprio e obrigando-o a selecionar um
dos clientes que pretende comunicar remotamente.
EnviarMensagem
No método sendMessage (String name, String message) adicionamos uma linha à tabela das
mensagens privadas com o nome do cliente que a envia e o respetivo conteúdo. Retornamos o nickname
de quem recebe a mensagem como pedido no enunciado.
PrivateMessaging
Esta interface serve unicamente para declarar o método sendMessage, podendo ser executado
pelo stub.
2. Respostas às Questões Específicas do Enunciado
Q1.
O nosso sistema não está implementado usando formatos externos de representação de dados.
Se se pretender adicionar um novo agente de utilizador, será necessário utilizar a linguagem de
programação JAVA, uma vez que esta já conta com a serialização que transforma os dados em conjuntos
de bytes.
Para tal, no “SESSION_UPDATE_REQUEST” temos de enviar uma String com o nickname e, através
do uso de booleans, indicar se o cliente deseja comunicar remotamente (RMI). No seguinte formato:
Se o boolean rmi for true, significa que o cliente deseja comunicar com outros através de
mensagens privadas beneficiando da comunicação remota. Caso contrário, ou seja, se o boolean for false
significa que o cliente não deseja comunicar com outros através de mensagens privadas nem receber as
mesmas, logo não beneficia da comunicação remota.
Se o cliente desejar comunicar remotamente, terá de ser criado um registo na porta 1099. Para
esse registo, precisa de criar um serviço com o nome “PrivateMessaging” que depois é associado a um
objeto remoto.
Quando o cliente desejar enviar uma mensagem privada, deve especificar o ip do destinatário da
mensagem e o nome do serviço(“PrivateMessaging”) para ser possível procurar no registo o objeto remoto
e enviar a mensagem.
Q2.
Para a implementação desta solução o servidor necessita de ter acesso a todos os objetos remotos
(stubs) dos clientes. Para tal, o cliente ao fazer o SESSION_UPDATE_REQUEST, este deve incluir o stub na
mensagem que envia.
Tendo acesso a esses objetos remotos, o servidor, sempre que quisesse notificar algum cliente,
seja mensagens privadas, mensagens gerais ou novas presenças, conseguiria executar os métodos dos
objetos remotos. Isto torna-se numa vantagem pois auxilia o servidor, não o sobrecarregando.
Q3.
O servidor tem acesso a todos os canais de comunicação (de entrada e saída de informação), de
todos os clientes ativos. Uma possível solução, era sempre que um cliente quisesse enviar uma
mensagem privada a outro cliente, efetuava um pedido ao servidor. O pedido teria de ser composto por,
no mínimo, dois parâmetros. No cabeçalho da mensagem, o cliente deve indicar se pretende enviar
mensagem privada ou não, bem como o conteúdo da mensagem e qual o recetor (nickname ou IP) da
mesma. Quando o servidor receber um pedido do cliente, deve analisar o cabeçalho e se se tratar de
uma mensagem privada, tem apenas de a encaminhar ao cliente destinatário, usando o “printWriter”.
Esta solução deixa muito a desejar relativamente a questões de segurança. Como o servidor
serve de intermediário entre o cliente emissor e recetor da mensagem, tem acesso ao conteúdo da
mesma, desrespeitando a privacidade entre clientes. Em termos de escalabilidade, se vários clientes
quisessem enviar qualquer tipo de mensagem para o servidor, este fica sobrecarregado, uma vez que
pode não ter capacidade para gerir todos os pedidos do cliente, aumentando a complexidade do sistema.