Relatório Trabalho Prático de Redes de Computadores II - Turma 2022-2 em 2023
Canhão UDP
Alunos
Luis Eduardo Mochenski Floriano | GRR 20190354Thiago Ruiz Aniceto | GRR 20190355
Introdução
Este relatório de trabalho tem como objetivo descrever o desenvolvimento de dois programas, um cliente e um servidor, com o objetivo de trocar mensagens UDP. O Trabalho com o nome de "Canhão UDP", foi desenvolvido na linguagem C e consiste num sistema em que o cliente envia N pacotes para o servidor, de forma que o servidor possa logar as perdas e inconsistências no recebibento deles. A sequência na troca de mensagens está no formato: 1,2,3,...,n.
Desenvolvimento
A implementação do trabalho foi realizada inteiramente na linguagem C, sendo utilizados apenas dois arquivos cliente.c e servidor.c.
Arquivos:
cliente.c.txt
servidor.c.txt
OBS.: Log foi realizado para 1000000 (um milhão) de dados, são extensos, na sessão de Resultado há uma versão simplificada contendo apenas as partes que provam o funcionamento correto do programa.
cliente_output.txt
servidor_output.txt
servidor_local_output.txt
A compilação e execução pode ser feita da seguinte forma:
$ gcc cliente.c -o cliente
$ gcc servidor.c -o servidor
$ ./cliente <nome-servidor> <porta> <quantidade_dados>
$ ./servidor <porta>
Cliente
Para comunicação:
- Chama o resolvedor DNS "gethostbyname(host)"
- Abertura do socket com a função "sock(...)"
- Envio de dados gerenciais "sendto(...)"
- Envio de N pacotes para o servidor com N chamadas de "sendto(...)"
- Fechamento de conexão enviando string "fim" (dez vezes) para o servidor com "sendto(...)"
Logs forma implementados em todas as etapas.
O Passo 4 adiciona um "sendto(...)" inicial contendo a string "total=n,". Dessa forma podemos informar ao Servidor, quantos dados ele deve esperar receber. Este passo é realizado com o intúito de melhorar o log do servidor, pois podemos comparar quantos dados foram enviados vs recebidos ao final da execução.
O Passo 5 consiste em um for com dados de 1 até N, onde é enviado a string "a" onde a ∈ [1..N].
Servidor
Para a abertura e fechamento de conexão foram realizados as seguintes etapas:
- Chama o resolvedor DNS "gethostbyname(host)"
- Abertura do socket_escuta com a função "sock(...)"
- Executa o bind "bind(...)"
- A aplicação entra em loop para receber as mensagens "while(1) {recvfrom(...)}"
- Finaliza a Conexão ao receber "fim"
Logs foram implementados em todas as etapas.
O Passo 4 funciona em três partes. A primeira consiste em identificar a string "total=n", para alocar um array de tamanho N - este passo só ocorre na primeira iteração do laço. A segunda, ocorre em todos as iterações, adiciona elementos do buffer no array. A última parte, detecta itens faltantes e fora de ordem no array.
O Passo 5, apenas quebra o loop ao receber "fim"
Detalhes de implementação
Algumas funções adicionais foram implementadas para facilitar o log e detecção de erros ao longo da execução, são elas:
log_mensagem
Printa uma mensagem no seguinte formato para a saida padrão:
onde mensagem é uma string
registra_dados_recebidos
Recebe um buffer "12032" e adiciona o item 12032 em um array chamado (recebidos).
verifica_fora_de_ordem_e_loga
Verficia se o array (recebidos) possui algum dado fora de ordem.
De forma simples: recebidos[i+1] > recebidos[i],
caso contrário loga mensagem com elementos fora de ordem e incrementa um contador.
Ao final retorna este contador.
verifica_dados_faltantes_e_loga
Verficia se o array (recebidos) possui algum dado faltante.
De forma simples: recebidos[i] == recebidos[i + 1] - 1, caso contrário loga mensagem com elementos faltantes
e incrementa um contador com |recebidos[i] - recebidos[i + 1]| - 1.
Ao final retorna este contador.
Log
Diversos logs foram implementados na aplicação para cada uma das etapas descritas no servidor e cliente. Cada dado enviado e recebido é logado. Caso haja algum fora de ordem ou faltante, também é logado.
Resultado
Para realizar os testes forma utilizadas as máquinas do laboratório do Dinf, usando ssh.
A máquina Cliente se encontrava na máquina cpu1
A máquina Servidor se encontrava na máquina orval
Form realizados testes com 1000000 (um milhão) de pacotes, para detectar erros na aplicação.
No cliente o log resumido foi:
[14:46:37.151]: Inicia canhão UDP com 1000000 pacotes
[14:46:37.151]: Envia quantos dados o Servidor deve esperar receber.
[14:46:37.151]: Enviando total=1000000
[14:46:37.151]: Enviando 1
[14:46:37.151]: Enviando 2
...
[14:46:55.903]: Enviando 999998
[14:46:55.903]: Enviando 999999
[14:46:55.903]: Enviando 1000000
[14:46:55.903]: Enviando fim
[14:46:55.903]: Fim canhão UDP, 1000000 pacotes enviados
No servidor o log resumido foi:
Utilizando os comandos grep, tail e head para verificar logs:
Para visualizar cabecalho:
$ head 10 servidor_output.txt
Para encontrar itens faltantes:
$ grep -rnw 'servidor_output.txt' -e 'Há' -B 5
Para encontrar itens fora de ordem:
$ grep -rnw 'servidor_output.txt' -e 'O item' -B 5
Para visualizar fim do arquivo (resultados):
$ tail -10 servidor_output.txt
O resultado simplificado com o auxílio destes programas foi:
[14:51:34.249]: Recebendo[tam: 13]: total=1000000
[14:51:34.249]: Registra quantos dados o Servidor deve esperar receber total = 1000000
[14:51:34.249]: Recebendo[tam: 1]: 1
[14:51:34.249]: Recebendo[tam: 1]: 2
...
[14:51:39.477]: Recebendo[tam: 6]: 204929
[14:51:39.477]: Recebendo[tam: 6]: 204950
[14:51:39.477]: Há 20 item(ns) faltantes entre 204929 e 204950
...
[14:51:53.002]: Recebendo[tam: 7]: 1000000
[14:51:53.002]: Recebendo[tam: 3]: fim
[14:51:53.002]: Recebeu fim
[14:51:53.002]: Fim canhão UDP, 994358 pacotes recebidos
[14:51:53.002]: Métricas do resultado final canhão UDP
[14:51:53.002]: Total de dados recebidos 994358 / esperava-se 1000000 Taxa: 99.435799%
[14:51:53.002]: Total de dados fora de ordem 0
[14:51:53.002]: Total de dados faltantes 5642
Observamos que não houve itens fora de ordem em ambiente remoto utilizado as máquinas orval e cpu1, apenas localmente.
Para comprovar o funcionamento, abaixo está um log do servidor local (executando cliente e servidor na mesma máquina).
[14:59:05.306]: Recebendo[tam: 13]: total=1000000
[14:59:05.306]: Registra quantos dados o Servidor deve esperar receber total = 1000000
[14:59:05.306]: Recebendo[tam: 1]: 1
...
[14:59:05.873]: Recebendo[tam: 5]: 54794
[14:59:05.873]: Recebendo[tam: 5]: 54795
[14:59:05.873]: Recebendo[tam: 5]: 57020
[14:59:05.873]: Há 2224 item(ns) faltantes entre 54795 e 57020
[14:59:05.873]: Recebendo[tam: 5]: 54796
[14:59:05.873]: O item 57020 está fora de ordem!
[14:59:05.873]: Há 2223 item(ns) faltantes entre 57020 e 54796
[14:59:05.873]: Recebendo[tam: 5]: 54797
[14:59:05.873]: Recebendo[tam: 5]: 54798
...
[14:59:16.159]: Recebendo[tam: 7]: 1000000
[14:59:16.159]: Recebendo[tam: 3]: fim
[14:59:16.159]: Recebeu fim
[14:59:16.159]: Fim canhão UDP, 994256 pacotes recebidos
[14:59:16.159]: Métricas do resultado final canhão UDP
[14:59:16.159]: Total de dados recebidos 994256 / esperava-se 1000000 Taxa: 99.425598%
[14:59:16.159]: Total de dados fora de ordem 46
[14:59:16.159]: Total de dados faltantes 5744
É interessante notar que localmente a taxa de transmissão 99.425598% foi similar ao de 99.435799% dos testes remotos.
Conclusão
O Canhão UDP proposto foi implementado e está funcional. Todas as detecções de erros foram logadas juntamente com as métricas ao final da transmissão. Foi possível observar os pacotes faltantes e desordenados na transmissão ao testar o funcionamento do canhão UDP remoto e local, indicando que a aplicação funcionou como esperado ao detectar e logar tais ocorrências.