Vous êtes sur la page 1sur 13

Universidade Federal de Minas Gerais

Graduação em Engenharia Elétrica


ELT091 — Redes TCP IP

Trabalho 1

Danilo P. Lima,
Lucas S. Lima,
Renata A. A. Teles

Professor Luciano de Errico

13 de Abril de 2018
Conteúdo
1 Introdução 3

2 Exercı́cio 32 3
2.1 Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Testes e Análise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Exercı́cio 33 8
3.1 Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Testes e Análise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Conclusão 12

2
1 Introdução
Houve, nas últimas décadas, um grande crescimento das redes de computadores, e, prin-
cipalmente a popularização da Internet, que nada mais é do que a interligação de diversas
redes. Assim como nas redes locais, todo informação na Internet é gerada por aplicações
rodando em hosts servidores e clientes.

Para que seja possı́vel a troca de informações entre aplicações servidoras e clientes,
é necessário que exista, primeiramente, algum protocolo de comunicação a ser seguido
e, em seguida, uma estrutura no kernel dos sistemas operacionais que possibilite a uti-
lização dos recursos computacionais para a comunicação na Internet.

Neste trabalho, apresentaremos dois exemplos do funcionamento de uma comunicação


entre uma aplicação servidora e aplicações clientes. O protocolo de comunicação utilizado
será o TCP/IP e toda a programação será feita em linguagem C, acessando a estrutura
de Sockets do kernel do Linux. Todas as estações estarão conectadas em uma rede local
Wireless.

2 Exercı́cio 32
Eis o enunciado do exercı́cio 32 do capı́tulo um do livro Redes de Computadores [1]:

Obtenha e construa o programa de exemplo usando sockets denominado simplex-talk,


mostrado no texto. Inicie um servidor e um cliente, em janelas separadas. Enquanto
o primeiro cliente estiver sendo executado, inicie 10 outros clientes que se conectam ao
mesmo servidor; esses outros clientes provavelmente deverão ser iniciados em segundo
plano, com sua entrada redirecionada para um arquivo. O que acontece com esses 10
clientes? Seus connects() falham, esgotam o tempo limite ou têm sucesso? Alguma outra
chamada é bloqueada? Agora encerre o primeiro cliente. O que acontece? Experimente
isso também com o valor MAX PENDING do servidor definido como 1.

2.1 Código
• Servidor
1 # include < stdio .h >
2 # include < sys / types .h >
3 # include < sys / socket .h >
4 # include < netinet / in .h >
5 # include < netdb .h >
6 # include < stdlib .h >
7 # include < string .h >
8 # include < unistd .h >
9

10 # define SERVER_PORT 54321

3
11 # define MAX_PENDING 1
12 # define MAX_LINE 256
13

14 int main ( int argc , char const * argv []) {


15 struct sockaddr_in sin ;
16 char buf [ MAX_LINE ];
17 int len ;
18 int s , new_s ;
19

20 /* Monta a estrutura de dados do endereco */


21 bzero (( char *) & sin , sizeof ( sin ) ) ;
22 sin . sin_family = AF_INET ;
23 sin . sin_addr . s_addr = INADDR_ANY ;
24 sin . sin_port = htons ( SERVER_PORT ) ;
25

26 /* Prepara a abertura passiva */


27 if (( s = socket ( PF_INET , SOCK_STREAM , 0) ) < 0) {
28 perror ( " simplex - talk : socket " ) ;
29 exit (1) ;
30 }
31 if (( bind (s , ( struct sockaddr *) & sin , sizeof ( sin ) ) ) < 0) {
32 perror ( " simplex - talk : bind " ) ;
33 exit (1) ;
34 }
35 listen (s , MAX_PENDING ) ;
36 len = sizeof ( sin ) ;
37 /* Espera conexao , depois recebe e imprime o texto */
38 while (1) {
39 if (( new_s = accept (s , ( struct sockaddr *) & sin , & len ) ) <
0) {
40 perror ( " simplex - talk : accept " ) ;
41 exit (1) ;
42 }
43 while ( len = recv ( new_s , buf , sizeof ( buf ) , 0) )
44 fputs ( buf , stdout ) ;
45 close ( new_s ) ;
46 }
47

48 return 0;
49 }

• Cliente
1 # include < stdio .h >
2 # include < sys / types .h >
3 # include < sys / socket .h >
4 # include < netinet / in .h >

4
5 # include < netdb .h >
6 # include < stdlib .h >
7 # include < string .h >
8 # include < unistd .h >
9 # include < arpa / inet .h >
10

11 # define SERVER_PORT 54321


12 # define MAX_LINE 256
13

14 int main ( int argc , char const * argv []) {


15 FILE * fp ;
16 struct sockaddr_in sin ;
17 char * host ;
18 char buf [ MAX_LINE ];
19 int s , new_s ;
20 int len ;
21

22 if ( argc == 2) {
23 host = ( char *) argv [1];
24 } else {
25 fprintf ( stderr , " usage : simplex - talk host \ n " ) ;
26 exit (1) ;
27 }
28

29 bzero (( char *) & sin , sizeof ( sin ) ) ;


30 sin . sin_family = AF_INET ;
31 sin . sin_port = htons ( SERVER_PORT ) ;
32 sin . sin_addr . s_addr = inet_addr ( host ) ;
33

34 if (( s = socket ( PF_INET , SOCK_STREAM , 0) ) <0) {


35 perror ( " simplex - talk : socket " ) ;
36 exit (1) ;
37 }
38 if ( connect (s , ( struct sockaddr *) & sin , sizeof ( sin ) ) <0) {
39 perror ( " simplex - talk : connect " ) ;
40 close ( s ) ;
41 exit (1) ;
42 }
43

44 while ( fgets ( buf , sizeof ( buf ) , stdin ) ) {


45 buf [ MAX_LINE -1] = ’ \0 ’;
46 len = strlen ( buf ) +1;
47 send (s , buf , len ,0) ;
48 }
49

50 return 0;

5
51 }

Nos preâmbulos dos códigos, definem-se a porta TCP SERVER PORT e o tamanho
máximo para o buffer MAX LINE. No servidor, define-se, também, o tamanho máximo
da fila de espera. Em seguida, cria-se o soquete e o associa ao endereço do servidor e à
porta TCP.

O servidor prepara a escrita passiva e utiliza o comando listen, deixando-o disponı́vel


para quando um dos clientes solicitar a conexão, por meio do comando connect. Antes
desses comandos, checa-se se houve algum erro na criação do soquete ou na sua asso-
ciação com o endereço.

Estabelecida a conexão, o usuário da aplicação cliente pode digitar o que deseja. Ao


pressionar ENTER, os comandos do loop while(fgets(buf, sizeof(buf ), stdin)) são exe-
cutados, enviado o que foi digitado para o servidor. Este, ao receber a informação do
cliente por meio do comando recv, imprime-a na tela.

Após a troca de informações, o servidor termina a conexão com o comando close, e


volta ao estado de abertura passiva, esperando uma nova conexão.

2.2 Testes e Análise


O teste proposto para essa questão foi verificar o funcionamento da comunicação quando
diversos hosts clientes tentavam se conectar ao servidor.

A figura abaixo mostra o terminal do primeiro host cliente a se conectar ao servidor,


enviando suas mensagens.

Figura 1: Mensagens enviadas pela aplicação cliente.

A figura abaixo mostra a recepção das mensagens vindas do primeiro host a se conec-
tar.

6
Figura 2: Mensagens recebidas pela aplicação servidora.

Durante a permanência da conexão do primeiro host cliente, os demais ficavam em uma


fila de espera, sendo que qualquer mensagem enviada não era recebida pela aplicação
servidora. Uma vez que o primeiro host cliente se desconectava, um novo membro da
fila assumia a conexão e transmitia suas mensagens pendentes. A figura abaixo mostra
a mensagem do host em espera.

Figura 3: Mensagens do segundo host em espera.

A figura abaixo mostra o recebimento das mensagens de um novo host cliente após a
desconexão do primeiro host a se conectar.

Figura 4: Mensagens do segundo host recebidas pela aplicação servidora.

7
A mudança no tamanho da fila de espera - valor de MAX PENDING - de 5 para 1
não surtiu efeito diferente devido ao fato de o experimento envolver apenas 2 clientes.
Dessa forma, havia exatamente 1 na fila. Se existisse um terceiro cliente, este já não
conseguiria estabelecer conexão se o tamanho máximo da fila for 1.

3 Exercı́cio 33
A seguir, tem-se o enunciado do exercı́cio 33:

Modifique o programa baseado em sockets denominado simplex-talk de modo que, toda


vez que o cliente enviar uma linha ao servidor, este enviará a linha de volta ao cliente.
O cliente (e o servidor) agora terá que fazer chamadas alternadas a recv() e send().

3.1 Código
• Servidor
1 # include < stdio .h >
2 # include < sys / types .h >
3 # include < sys / socket .h >
4 # include < netinet / in .h >
5 # include < netdb .h >
6 # include < stdlib .h >
7 # include < string .h >
8 # include < unistd .h >
9

10 # define SERVER_PORT 54321


11 # define MAX_PENDING 5
12 # define MAX_LINE 256
13

14 int main ( int argc , char const * argv []) {


15 struct sockaddr_in sin ;
16 char buf [ MAX_LINE ];
17 int len ;
18 int s , new_s ;
19

20 bzero (( char *) & sin , sizeof ( sin ) ) ;


21 sin . sin_family = AF_INET ;
22 sin . sin_addr . s_addr = INADDR_ANY ;
23 sin . sin_port = htons ( SERVER_PORT ) ;
24

25 if (( s = socket ( PF_INET , SOCK_STREAM , 0) ) < 0) {


26 perror ( " simplex - talk : socket " ) ;
27 exit (1) ;
28 }
29 if (( bind (s , ( struct sockaddr *) & sin , sizeof ( sin ) ) ) < 0) {

8
30 perror ( " simplex - talk : bind " ) ;
31 exit (1) ;
32 }
33 listen (s , MAX_PENDING ) ;
34 len = sizeof ( sin ) ;
35 while (1) {
36 if (( new_s = accept (s , ( struct sockaddr *) & sin , & len ) ) <
0) {
37 perror ( " simplex - talk : accept " ) ;
38 exit (1) ;
39 }
40 while ( len = recv ( new_s , buf , sizeof ( buf ) , 0) ) {
41 fputs ( buf , stdout ) ;
42 buf [ MAX_LINE -1] = ’ \0 ’;
43 send ( new_s , buf , len ,0) ; // Retransmite a mensagem
recebida em buf
44 }
45 close ( new_s ) ;
46 }
47 return 0;
48 }

• Cliente
1 # include < stdio .h >
2 # include < sys / types .h >
3 # include < sys / socket .h >
4 # include < netinet / in .h >
5 # include < netdb .h >
6 # include < stdlib .h >
7 # include < string .h >
8 # include < unistd .h >
9 # include < arpa / inet .h >
10

11 # define SERVER_PORT 54321


12 # define MAX_LINE 256
13

14 int main ( int argc , char const * argv []) {


15 FILE * fp ;
16 struct sockaddr_in sin ;
17 char * host ;
18 char buf [ MAX_LINE ];
19 int s , new_s ;
20 int len ;
21

22 if ( argc == 2) {
23 host = ( char *) argv [1];

9
24 } else {
25 fprintf ( stderr , " usage : simplex - talk host \ n " ) ;
26 exit (1) ;
27 }
28

29 bzero (( char *) & sin , sizeof ( sin ) ) ;


30 sin . sin_family = AF_INET ;
31 sin . sin_port = htons ( SERVER_PORT ) ;
32 sin . sin_addr . s_addr = inet_addr ( host ) ;
33

34 if (( s = socket ( PF_INET , SOCK_STREAM , 0) ) <0) {


35 perror ( " simplex - talk : socket " ) ;
36 exit (1) ;
37 }
38 if ( connect (s , ( struct sockaddr *) & sin , sizeof ( sin ) ) <0) {
39 perror ( " simplex - talk : connect " ) ;
40 close ( s ) ;
41 exit (1) ;
42 }
43

44 while ( fgets ( buf , sizeof ( buf ) , stdin ) ) {


45 buf [ MAX_LINE -1] = ’ \0 ’;
46 len = strlen ( buf ) +1;
47 send (s , buf , len ,0) ;
48 recv (s , buf , len ,0) ;
49 fputs ( buf , stdout ) ;
50 }
51

52 return 0;
53 }

Em relação ao exercı́cio anterior, acrescentou-se ao código do servidor, logo após ao


comando que imprime a mensagem recebida na tela, o comando send(new s,buf,len,0),
que retransmite o que foi recebido.

No código do cliente, acrescentaram-se os comandos recv(s,buf,len,0) - recebimento de


mensagem proveniente do servidor - e fputs(buf,stdout) - impressão de mensagem na tela
- após o envio da mensagem.

10
3.2 Testes e Análise
Nesta nova situação, a principal diferença está na aplicação servidora retransmitir a
mensagem recebida ao host de origem. Seguimos o mesmo teste anterior, com diversos
hosts clientes conectados em uma fila de espera.

A figura abaixo mostra a recepção de uma mensagem vinda do primeiro host a se


conectar à aplicação servidora.

Figura 5: Mensagens recebidas pela aplicação servidora.

A figura abaixo mostra o terminal da aplicação cliente, evidenciando a recepção de


cada linha enviada, demonstrando o funcionamento correto.

Figura 6: Mensagens enviadas e recebidas pela aplicação cliente.

Novamente, diversos hosts clientes estavam em uma fila de espera para se conectar a
aplicação servidora.
Assim que o primeiro host se desconectou, o primeiro host da fila transmitiu suas
mensagens.

11
Figura 7: Mensagens do segundo host em espera.

Figura 8: Mensagens recebidas pela aplicação servidora.

4 Conclusão
Neste trabalho, foram desenvolvidos os códigos de uma aplicação servidora e de aplicações
clientes que resolvessem a duas questões propostas: a primeira onde a aplicação servidora
receberia mensagens de um host cliente conectado e a segunda onde além de receber a
mensagem, a aplicação servidora retransmitiria para o cliente uma cópia da informação.
Com este trabalho foi possı́vel entender, de maneira simplificada, como um servidor
comunica através de uma conexão TCP/IP, e observamos um método de gerenciamento
de múltiplas conexões que é o de criar uma lista de espera a medida que novos host
tentam se conectar, e assim que o host se desconecta ele trata o próximo da lista de
espera.

12
Todos os códigos executaram corretamente, evidenciando o funcionamento da fila de
espera para a conexão com o servidor.

Referências
[1] L.L. Peterson and B.S. Davie. Redes de Computadores - Uma abordagem de sistemas.
Campus, 2013.

13