Escolar Documentos
Profissional Documentos
Cultura Documentos
1ª 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
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.
IPInfo
Esta classe serve para guardar a informação relativa ao cliente: IP, nickname e regista
quando foi a última vez que este efetuou um pedido ao servidor. 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, e a data mais recente na qual o cliente foi visto, ou
seja, a última vez que efetuou um pedido ao servidor.
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 e a porta do respetivo endereço. 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. 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.
Ao clicar no botão “Enviar”, o método enviarMensagem(String mensagemEnviar) é
chamado e enviamos para o servidor um pedido do tipo “AGENT_POST” que contém a
mensagem em questão.
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.
Já no método receberSessionUpDate() vamos guardar as informações das presenças e
das mensagens em dois arrays distintos. Inicialmente percorremos o array das presenças para
que possamos listar os clientes presentes na interface do cliente, adicionando assim a
quantidade de linhas na tabela correspondente ao número de utilizadores ativos. Realizamos o
mesmo processo para as mensagens: para cada elemento contido no array vamos adicionar uma
linha na tabela das mensagens com o respetivo conteúdo (nickname: post). Após isto alteramos
a visibilidade do botão “Conectar”, metendo este como não visível.
Q1.
Sempre que o agente de utilizador quiser enviar uma mensagem para publicar no feed,
terá de o fazer enviando para o servidor no seguinte formato: "AGENT_POST:" +
mensagemEnviar.
Para receber mensagens vindas do servidor, o cliente precisa de ter uma thread a correr
enquanto a ligação com o cliente estiver ativa, para que este possa receber as mensagens vindas
do servidor. Existem dois tipos de mensagem: “SESSION_UPDATE” e “SESSION_TIME_OUT”.
O primeiro tipo de mensagem é recebido sempre que um cliente envia uma mensagem
para o servidor seja ela "SESSION_UPDATE_REQUEST" ou "AGENT_POST". Existe uma ordem
para receber informação vinda do servidor sempre que recebemos uma mensagem do tipo
“SESSION_UPDATE”. Em primeiro lugar, recebemos o número de utilizadores presentes na lista
de presenças. O agente de utilizador ficando a saber quantos números de utilizadores estão
presentes, receberá essa quantidade em nicknames. De seguida, recebe o número de
mensagens mais recentes e fica a saber quantas mensagens vai receber a seguir. Após isso, o
conteúdo de cada mensagem é recebido.
Se for do tipo “SESSION_TIME_OUT”, significa que o tempo estipulado foi excedido sem
o cliente ter feito nenhum pedido e a ligação é encerrada.
Q2.
O nosso sistema não está totalmente preparado para lidar com falhas de forma
autónoma. Testamos o nosso programa e corrigimos todas as que encontramos e incluímos
exceptions para prevenir potenciais erros, porém isto não significa que não tenhamos que
enfrentar falhas.
Q3.
No que diz respeito ao relógio global sentimo-nos limitados quando tentamos acertar
o cronómetro de cada um dos temporizadores (SESSION_TIME_OUT e o
SESSION_UPDATE_REQUEST). Isto ser-nos-ia útil para tanto o cliente como o servidor terem as
informações o mais atualizadas e sincronizadas possível.
A falta de segurança para já é uma limitação porque não está a ser tratada nesta fase,
no entanto, iremos lidar com a mesma numa próxima entrega.
Uma outra limitação é o envio das mensagens ser em String e não ser possível enviar o
objeto todo, como por exemplo um ArrayList.
Q4.
Existem diversos fatores que tornam uma solução mais adequada que a outra, assim
como também existem outros fatores que a tornam menos adequada. Devemos debruçar-nos
sobre esta questão e analisar o sistema e as suas necessidades para entendermos qual das
soluções é a mais indicada.
No entanto, para isto ser possível, iremos sobrecarregar o servidor e tornar mais
complexo o nosso sistema, tornando esta solução menos lapidada.
Por outro lado, esta solução apresenta algumas desvantagens. Sempre que houver
alguma alteração no sistema, o cliente não será informado, a não ser que requisite ao servidor,
correndo o risco de se fiar erroneamente na visualização desatualizada do sistema.
Q5.
O projeto que temos vindo a desenvolver recai sobre a comunicação entre clientes
ligados através de um servidor. Para que esta comunicação seja eficaz e seja estabelecida
corretamente, é necessário que as mensagens sejam entregues por ordem ao cliente e sem
perdas de informação.
No nosso ponto de vista, o protocolo UDP poderiam ser a melhor solução se o seguinte
fator fosse prioritário: A rapidez da conexão; se necessitássemos de uma conexão rápida,
como é necessário numa chamada de voz ou jogos, mesmo podendo sofrer algum tipo de
perda de dados, diríamos que o protocolo UDP cobre melhor essa funcionalidade.