Relatório Trabalho Prático de Redes de Computadores II - Turma 2022-2 em 2023

Canhão UDP

Alunos

Luis Eduardo Mochenski Floriano | GRR 20190354
Thiago 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>

A seguir serão descritos os funcionamentos do cliente e servidor, explicando também decisões específicas de implementação. Ao final detalharemos a estruturação do log.

Cliente

Para comunicação:

  1. Chama o resolvedor DNS "gethostbyname(host)"
  2. Abertura do socket com a função "sock(...)"
  3. Envio de dados gerenciais "sendto(...)"
  4. Envio de N pacotes para o servidor com N chamadas de "sendto(...)"
  5. 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:

  1. Chama o resolvedor DNS "gethostbyname(host)"
  2. Abertura do socket_escuta com a função "sock(...)"
  3. Executa o bind "bind(...)"
  4. A aplicação entra em loop para receber as mensagens "while(1) {recvfrom(...)}"
  5. 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:

[%H:%M:%S]: <mensagem>\n;

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.150]: Abre conexão com socket descr
[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:31.250]: Inicia recebimento de dados canhão UDP
[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:03.924]: Inicia recebimento de dados canhão UDP
[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.