Anda di halaman 1dari 13

Universidade Federal de Minas Gerais

EEE933 - Tópicos Especiais em Engenharia de


Computação e Telecomunicações - Redes TCP IP

Soquetes UNIX - TCP

Adriano Felicio de Freitas


Renata Alves Antunes Teles

Professor Luciano de Errico

7 de Maio de 2018
Conteúdo
1 Introdução 3

2 Experimento 1 4
2.1 Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Testes e Análise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

3 Experimento 2 8
3.1 Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Testes e Análise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4 Conclusões 13

2
1 Introdução
Um socket é uma abstração que pode ser entendida como um ponto de comunicação
onde uma aplicação se conecta com a outra aplicação. Ou seja, os sockets abstraem as
camadas de rede e dá às aplicações dos hosts de origem e destino a impressão de que
elas estão diretamente ligadas. Isso é chamado comunicação lógica, ilustrado na figura
1.

Figura 1: Interface de sockets [1]

Existe uma nomenclatura especı́fica para se referir a cada “ponta” da comunicação. O


”servidor”(server ) fica disponı́vel e à espera de pedidos de conexões, e fornece fornece cer-
tos tipos de serviços aos requerentes. Estes, por sua vez, são chamados ”clientes”(client),
que solicitam a conexão ao servidor para fazer alguma requisição. É importante dizer
que não são os hosts que distinguem quem é servidor e quem é cliente, mas sim a forma
como cada aplicação usa os sockets.

Existem dois tipos de sockets: Socket Stream (TCP) ou SOCK DGRAM (UDP). Os
sockets TCP fornecem uma comunicação confiável (garantia da entrega, sem erros, de
pacotes), enquanto os sockets UDP não dão garantia na entrega de pacotes mas, devido
à sua simplicidade, faz com que a entrega dos pacotes seja mais ágil.

Neste trabalho, conhece-se, de uma maneira mais aprofundada, o funcionamento de


uma comunicação entre uma aplicação servidora e uma aplicações clientes. O protocolo
de transporte utilizado é o TCP e a implementação foi feita em linguagem C. As estações

3
utilizavam o sistema operacional Ubuntu 16.04 LTS e estavam conectadas por uma rede
Ethernet.

2 Experimento 1
O primeiro experimento consistiu em fazer o exercı́cio 32 do capı́tulo um do livro-texto
[2]. O enunciado é o seguinte:

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 Scripts
• 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 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 ) ;

4
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 >
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 ];

5
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;
51 }

Nos preâmbulos dos códigos, definem-se a porta T CP SERV ER P ORT (alterada de


5432 para 54321) e o tamanho máximo para o buffer M AX LIN E = 256 (8 bits).
No script do servidor, especifica-se, também, o tamanho máximo da fila de espera
M AX P EN DIN G = 1, pois o experimento foi feito com apenas dois clientes. Em
seguida, cria-se o soquete que se associa ao endereço do servidor e à porta TCP.

O servidor, por meio do comando listen, fica disponı́vel para quando um dos clientes
solicitar a conexão com o comando connect. Antes desses comandos, checa-se se houve
algum erro na criação do soquete ou na sua associação com o endereço.

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


pressionar ENTER, a mensagem digitada é armazenada em um buffer e enviada ao ser-

6
vidor utilizando o comando send. 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. Estes e os testes do experimento
dois foram executados em computadores com sistema operacional Ubuntu 16.04 LTS co-
nectados por uma rede Ethernet.

A figura 2 mostra o terminal do primeiro host cliente conectado ao servidor contendo a


mensagem enviada.

Figura 2: Mensagem enviada pela aplicação cliente.

A figura 3 mostra a recepção das mensagens vindas do primeiro host a se conectar.

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

Durante a permanência da conexão do primeiro host cliente, o segundo ficava em uma


fila de espera, sendo que qualquer mensagem enviada não era recebida pela aplicação
servidora. Isso se deve ao fato de os comandos send e recv serem bloqueantes, ou seja,
admitirem uma conexão por vez. Uma vez que o primeiro host cliente se desconectava,

7
o cliente que estava na fila estabelecia uma conexão com o servidor e transmitia suas
mensagens pendentes. A figura 4 mostra a mensagem enviada pelo host em espera.

Figura 4: Mensagens do segundo host em espera.

A figura 5 mostra o recebimento das mensagens do segundo cliente após a desconexão


do primeiro.

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

Apesar da mudança no tamanho da fila de espera de 1 para 0, não se observou dife-


rença nos testes.

3 Experimento 2
A seguir, tem-se o enunciado do exercı́cio 33 do capı́tulo um do livro-texto, atividade a
ser realizada no experimento em questão:

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 Scripts
• Servidor

8
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) {
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 }

9
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];
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 ) ;

10
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. A retransmissão da mensagem representa um ACK,
para que o transmissor, neste caso o cliente, tenha a confirmação de que sua mensagem
chegou ao destino.

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.

11
3.2 Testes e Análise
Nesta nova situação, a principal diferença está na aplicação servidora retransmitir a men-
sagem recebida ao host de origem. Dessa vez, o teste foi realizado com apenas um host se
conectando ao servidor, o que era suficiente para verificar o que foi pedido na questão 33.

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


tar à aplicação servidora.

Figura 6: Mensagem recebida pela aplicação servidora.

Na figura 7, que apresente o terminal da aplicação cliente, verifica-se a recepção da


mensagem enviada, demonstrando o funcionamento correto do código.

Figura 7: Mensagem enviada e recebida pela aplicação cliente.

12
4 Conclusões
Neste trabalho, foram desenvolvidos códigos para uma aplicação servidora e para aplicações
clientes a fim de solucionar as duas questões propostas: a primeira, onde o servidor re-
cebe mensagens de mais de um cliente; e a segunda na qual, ao receber uma mensagem,
o servidor a retransmite para o cliente.

Com este trabalho foi possı́vel entender, de maneira simplificada, a comunicação por
meio do protocolo de transporte TCP.

Em todos os códigos implementados utilizou-se a interface de sockets. Como eles abs-


traem as camadas de rede, não foi necessário se preocupar com detalhes da rede durante
a implementação dos códigos.

Referências
[1] Gustavo Pantuza. O que são e como funcionam os sockets, 2017.

[2] L.L. Peterson and B.S. Davie. Redes de Computadores - Uma abordagem de sistemas.
Campus, 2013.

13

Anda mungkin juga menyukai