Atividade 2 (obrigatória): Serviço de eco usando sockets TCP

Atualizado em 27/08/08
  1. Modifique os programas client_echo.c e server_echo.c que V. desenvolveu na atividade 1, de forma a eliminar o erro conceitual de supor que as funções recv() (ou read()) e send() (ou write()) com sockets TCP preservam fronteiras de mensagens (no caso, linhas ASCII delimitadas por lf)(*). Para este fim V. deve utilizar as funções fgets() e fputs() (veja man fgets e man fputs) da biblioteca padrão (standard library) da linguagem C da seguinte forma:
    1. A partir do descritor de socket conectado crie dois   FILE pointers, rsock e wsock, um para leitura e e outro para escrita, respectivamente, lembrando que uma conexão TCP suporta comunicação bidirecional (veja man fdopen).
    2. No lugar das funções read()/recv() e write()/send() utilize as funções fgets() e fputs() utilizando como argumentos os FILE * rsock e wsock obtidos via   fdopen(). Observe que agora o fechamento da conexão pelo cliente será detetado no servidor pelo retorno do valor NULL pela função fgets().
    3. Após invocar as funções fgets() e fputs sobre rsock() e wsock, V. deve chamar   fflush(rsock) e fflush(wsock), respectivamente. As razões para isto foram detalhadas em aula.
    4. V. deve testar o seu cliente modificado executando o   server_echo.c numa rede distinta da do laboratório.
    5. Faça 2 testes redirecionando a saída padrão para /dev/null e tomando como entrada:
      (i) o arquivo /etc/termcap
      (ii)um arquivo ps grande qualquer, por exemplo,   notas_aula.ps
      Apresente na saida de erro (stderr) as mesmas estatisticas da atividade 1 verificando a correção dos resultados via utilitário wc.
    6. Apresente numa pequena tabela as 2 medidas de tempo junto com a estimativa via ping() (veja a seguir) e a saída do utilitário   wc. Submeta também os fontes dos dois programas, incluindo na impressão os nomes dos participantes.

    O programa da atividade 1 client_echo.c tem a característica "Pedido-Resposta", ou seja: o cliente só envia a próxima linha ao servidor após receber do mesmo a linha anterior e enviá-la para a saída padrão (ou para /dev/null). Esta é a razão básica pela qual ele funciona, apesar do erro conceitual citado anteriormente e discutido em aula). Nesse caso, o tempo da sua execução se aproxima do RTT (Round Trip Time) entre o cliente e o servidor multiplicado pelo número de linhas do arquivo. O utilitário   ping pemite obter uma medida aproximada do RTT. Use essa medida para estimar o tempo de eco dos dois arquivos nos testes feitos acima.

  2. Seria interessante o cliente poder enviar o arquivo tendo como limitação apenas a velocidade de processamento no kernel da pilha TCP e a vazão pela rede ("throughput") da conexão TCP. Isto pode ser feito de uma forma extremamente simples utilizando dois processos no programa cliente onde, digamos, o processo filho repetidamente lê uma linha da entrada padrão e a envia pelo socket e o processo pai repetidamente lê uma linha do socket e a envia para a saída padrão.
    Um problema com esta abordagem é que o processo filho deve sinalizar o servidor quando terminar de enviar o arquivo para o mesmo. Isto era feito antes simplesmente fechando a conexão (via close()). Infelizmente não vai funcionar neste caso, pois no momento do fechamento da conexão o processo pai ainda não recebeu todas as linhas que estão sendo ecoadas pelo servidor. Se a conexão for fechada pelo filho o pai perderá essas linhas e o sistema gerará um erro do tipo broken pipe (teste!). Por isto o processo filho deve executar um half close da conexão, isto é, fechar a conexão apenas para escrita invocando a função   shutdown(descritor_socket, SHUT_WR).
    O processo pai deve também esperar pelo término do filho, antes de emitir as suas estatísticas e terminar. Para isto ele pode usar a função do Unix   wait(NULL);

    Faça 2 testes redirecionando a saída padrão para /dev/null e tomando como entrada:
    (i) o arquivo /etc/termcap
    (ii)um arquivo ps grande, por exemplo, notas_aula.ps
    Inclua na tabela anterior as novas medidas de tempo. Eles devem ser substancialmente menores do que os obtidos na solução do tipo "pedido-resposta" do item 1 acima.
    Submeta o fonte do novo cliente e do servidor. Obs: não é necessário submeter o fonte do cliente do item 1: apresente apenas uma tabela com os resultados das diversas medidas de tempo.


    (*) Ao resolver esse problema, V. na verdade terá resolvido um problema mais genérico e interessante que é: "como implementar um protocolo de aplicação usando TCP onde mensagens são textuais, de tamanho variável e delimitadas por lf ?".