A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Introdução à Programação com Sockets Fernando Jorge Silveira Filho Daniel Sadoc Menasché

Apresentações semelhantes


Apresentação em tema: "Introdução à Programação com Sockets Fernando Jorge Silveira Filho Daniel Sadoc Menasché"— Transcrição da apresentação:

1 Introdução à Programação com Sockets Fernando Jorge Silveira Filho fernando@land.ufrj.br Daniel Sadoc Menasché sadoc@land.ufrj.br

2 Visão Geral Objetivos: – Realizar comunicação entre processos. – Entender como usar as funções da API sockets. Histórico da API sockets: desenvolvida na época da implementação dos protocolos TCP/IP pelo grupo da UC, Berkeley. Inicialmente iremos (re)ver alguns conceitos que serão importantes.

3 Rotinas do S.O. Sockets Comandos no Hardware Rotinas do Driver API Sockets Application Program Interface – elemento de ligação entre a aplicação e um sistema de mais baixo nível. Aplicação Transporte Rede Enlace de Dados Física Sistema Operacional (Kernel) Drivers e Hardware Aplicação Final

4 Conceitos Importantes Argumentos de Valor/Resultado Descritores de Arquivo Chamadas de Sistema de E/S Byte Ordering Portas e Endereços IP Associações & Conexões Serviços de Transporte

5 Argumentos de Valor/Resultado É possível (e comum) na linguagem C, utilizar o recurso de passagem de argumentos por referência para passar valores para uma função ao mesmo tempo em que se espera um valor de retorno na mesma variável. Exemplo: imagine uma função is_prime() que retorna 1 se um número apontado por um ponteiro n é primo e 0 (zero) se composto. Neste último caso, a função também retorna o número primo mais próximo do valor passado. Este segundo valor de retorno pode ser colocado no próprio endereço de memória referenciado por n. int is_prime( int * n ) { /* verifica se (*n) é primo e em caso negativo coloca o primo mais próximo em (*n) */ }

6 Descritores de Arquivos Números inteiros não negativos que são ponteiros para estruturas de dados do sistema que representam os arquivos abertos por um processo. 72615503921 x Descritores de Arquivo y z Imagem do ProcessoEstruturas do S.O. z z

7 Chamadas de Sistema de E/S Operações básicas que podem ser realizadas com arquivos. open, close, read, write. #include int open( const char * filename, int oflag,... ); int close( int fd ); size_t read( int fd, void * buffer, size_t n_bytes ); size_t write( int fd, void * buffer, size_t n_bytes );

8 Como um valor é armazenado na memória? Exemplo: 16909060 = 0x01020304 Para evitar conflitos entre hosts de arquiteturas diferentes é convencionado que informações de controle na rede são armazenadas em Big-Endian. Byte Ordering Big-EndianLittle-Endian 01 02 03 04 n n+1 n+2 n+3 EndereçoMemória 04 03 02 01 n n+1 n+2 n+3 EndereçoMemória

9 Endereços IP e Portas Endereços IP identificam hosts na rede TCP/IP e são formados por 4 bytes, normalmente chamado octetos por razões históricas. Portas são identificadores dos processos interessados em usar os recursos de rede em um host. Portas ocupam 2 bytes na memória. Ambos tipos de variáveis são armazenadas no byte order da rede (Big-Endian). 14616410239990 92A40A029C36 Notação Padrão Hexadecimal

10 Associações Um processo precisa associar um socket a um endereço para “avisar” ao sistema operacional que deseja receber dados que chegam ao host com o endereço de destino especificado. 602 789 1026 102 Processos 20 23 146.164.41.10 : 80 25 41 146.164.41.10 : 21 146.164.41.10 : 20 Endereço Associado Sockets 602 789 1026 102 Processos 20 23 146.164.41.10 : 80 25 41 146.164.41.10 : 21 146.164.41.10 : 20 146.164.41.10 : 56 Endereço Associado Sockets

11 Conexões Um socket pode ser conectado a um endereço diferente do seu próprio para que pacotes possam ser enviados por ele diretamente ao processo parceiro. 602 Processo 20 Endereço Associado Socket 200.128.40.12 : 10020 Endereço Conectado 146.164.41.10 : 13003

12 Serviços de Transporte A camada de transporte dos protocolos TCP/IP fornece duas opções de tipos de serviço: – Serviço orientado a conexão: utiliza o protocolo TCP (Transmission Control Protocol) e garante entrega e ordenação. – Serviço datagrama: utiliza o protocolo UDP (User Datagram Protocol) e não faz nenhuma garantia.

13 Esquema de uma Aplicação TCP Um par de aplicações que se comunicam por TCP em geral tem a seguinte forma: accept( ) close( ) connect( ) socket( ) bind( ) listen( ) close( ) Estabelecimento da Conexão TCP Cliente Servidor writen( ) readn( ) writen( ) readn( )

14 Esquema de uma Aplicação TCP Um par de aplicações que se comunicam por TCP em geral tem a seguinte forma: accept( ) close( ) connect( ) socket( ) bind( ) listen( ) close( ) Estabelecimento da Conexão TCP Cliente Servidor writen( ) readn( ) writen( ) readn( ) ter telefone associar número ao telefone ligar alarme responder chamada fazer chamada

15 Sockets - Criação Utiliza-se a chamada de sistema socket() : Parâmetros: – domain – o tipo de rede do socket. Usamos AF_INET (Address Family Internet). Outros tipos: AF_LOCAL, AF_INET6. – type – o tipo de serviço de transporte. Para sockets TCP usamos SOCK_STREAM, para UDP usamos SOCK_DGRAM. – protocol – o protocolo utilizado. Passando 0 (zero) é utilizado o padrão. A função retorna um descritor do socket criado ou –1 em caso de erro. #include int socket( int domain, int type, int protocol );

16 Sockets - Endereços Endereços IP e portas são armazenados em estruturas do tipo struct sockaddr_in. sin_family é o tipo do endereço. AF_INET deve ser usado. sin_port é a porta associada ao endereço. sin_addr é uma estrutura que contém o endereço IP (os 4 octetos). O endereço IP e a porta devem ser armazenados no byte order da rede (Big- Endian). #include struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; /*... outros campos */ }; struct in_addr { in_addr_t s_addr; }; uint16 htons( uint16 data_in_host_order ); uint16 ntohs( uint16 data_in_net_order ); uint32 htonl( uint32 data_in_host_order ); uint32 ntohl( uint32 data_in_net_order );

17 Sockets - Associação Utiliza-se a chamada de sistema bind() : Parâmetros: – sockfd – o descritor do socket. – my_addr – a estrutura com o endereço para ser associado. – addrlen – o tamanho da estrutura do endereço. A função retorna 0 (zero) em caso de sucesso ou –1 no caso de um erro. Erro comum: EADDRINUSE (“Address already in use”) #include int bind( int sockfd, struct sockaddr *my_addr, socklen_t addrlen );

18 Sockets – Traduzindo Endereços IP Para converter um endereço IP entre as formas de string e binária: A função inet_aton() converte um endereço na notação do ponto (1.2.3.4) para o formato binário em byte order de rede (como a struct sockaddr_in espera) e retorna 0 (zero) em caso de sucesso. A função inet_ntoa() faz a conversão oposta. #include int inet_aton(const char * str, struct in_addr * addrptr); char * inet_ntoa(struct in_addr addr);

19 Sockets – Resolvendo Nomes com DNS Para resolver nomes de hosts (www.land.ufrj.br) para endereços IP (146.164.47.193) usamos as rotinas de acesso ao serviço DNS: #include struct hostent * gethostbyname(const char * str); struct hostent { int h_length; /* tamanho do endereço */ char **h_addr_list; /* lista de endereços */ char *h_addr; /* primeiro endereço */ /*... outros campos */ }

20 Sockets TCP Sockets orientados a conexão com garantias de entrega e ordenação. É preciso estabelecer a conexão antes da troca de dados. O servidor cria um socket especial para “escutar” pedidos de conexão do cliente. Cada vez que o servidor aceita um pedido de conexão recebido no socket de escuta, um novo socket é criado para realizar a comunicação. Assim é possível para o servidor voltar a aceitar novos pedidos de conexão mais tarde (usando o socket de escuta).

21 Sockets TCP – listen() Para por um socket em modo de escuta usamos a chamada de sistema listen() : Parâmetros: – sockfd – o descritor do socket. – backlog – a soma das filas de conexões completas e incompletas. Este parâmetro é extremamente dependente da implementação do Sistema Operacional. O valor de retorno da função é 0 (zero) em caso de sucesso ou –1 caso contrário. #include int listen( int sockfd, int backlog );

22 Sockets TCP – accept() Esta função aceita pedidos de conexão pendentes ou fica bloqueada até a chegada de um. Parâmetros: – sockfd – o descritor do socket. – cliaddr – a estrutura onde será guardado o endereço do cliente. – addrlen – argumento valor/resultado com o tamanho da estrutura do endereço. O valor de retorno da função é um novo descritor (não negativo) em caso de sucesso ou –1 caso contrário. Cada novo descritor retornado por accept() está associado à mesma porta do socket de escuta. #include int accept( int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen );

23 Sockets TCP – send() Usada para enviar dados por um socket conectado. Parâmetros: – sockfd – o descritor do socket. – buffer – um ponteiro para os dados a serem enviados. – n_bytes – quantidade de bytes a serem enviados a partir do ponteiro buffer. – flags – opções para essa operação. O valor de retorno da função é a quantidade de bytes enviados em caso de sucesso ou –1 caso contrário. #include int send( int sockfd, void * buffer, size_t n_bytes, int flags );

24 Sockets TCP – recv() Recebe dados por um descritor conectado, ou bloqueia a execução até que algum dado chegue ao socket: Parâmetros: – sockfd – o descritor do socket. – buffer – um ponteiro para a área de memória onde devem ser armazenados os dados recebidos. – n_bytes – quantidade máxima de bytes a serem recebidos. – flags – opções para essa operação. O valor de retorno da função é a quantidade de bytes recebidos em caso de sucesso ou –1 caso contrário. #include int recv( int sockfd, void * buffer, size_t n_bytes, int flags );

25 Atenção para uma Armadilha! As funções read, recv, recvfrom, write, send e sendto podem retornar menos bytes do que a quantidade requisitada, e nenhuma mensagem de erro é retornada neste caso! Solução: ao usar as funções r ecv, recvfrom, send e sendto pode-se (deve- se!) passar como parâmetro a flag MSG_WAITALL.

26 Atenção para uma Armadilha! As funções read e write não devem ser utilizadas! Ao invés disto, deve-se usar as funções de “embrulho” (“wrap functions”) readn e writen. ssize_t readn(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nread; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( ( nread = read ( fd, ptr, nleft ) ) <=0 ) { if (errno == EINTR) nread = 0; else return (-1); } else if ( nread == 0) break; /* EOF */ nleft -= nread; ptr += nread; } return (n - nleft); }

27 #include int main( int argc, char ** argv ) { int sockfd; char recvline[30]; struct sockaddr_in servaddr; if( argc != 2 ) return –1; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); memset( &servaddr, 0, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(12345); inet_aton( argv[1], &servaddr.sin_addr ); connect( sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr) ); readn( sockfd, recvline, 30 ); fputs( recvline, stdout ); close( sockfd ); return 0; } Exemplo – Um Cliente TCP Inicializar a estrutura servaddr com zeros. Preencher os campos da estrutura servaddr :  sin_family com o tipo de endereço (AF_INET)  sin_port com a porta (12345) no byte order da rede.  sin_addr com o endereço passado como argumento para o programa. Conecta o socket com o endereço do servidor ( servaddr ). Isto irá estabelecer a conexão TCP entre o cliente e o servidor. Lê 30 bytes do socket. Esta função pode ler menos bytes se o outro lado (o servidor) fechar a conexão. Imprime os dados recebidos na saída padrão. Fecha o socket. Criar um socket para rede TCP/IP ( AF_INET ), orientado a conexão ( SOCK_STREAM ), usando TCP (o protocolo padrão dado por 0 ).

28 #include int main( int argc, char ** argv ) { int listenfd, connfd, size; struct sockaddr_in myaddr, cliaddr; listenfd = socket( AF_INET, SOCK_STREAM, 0 ); memset( &myaddr, 0, sizeof(myaddr) ); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(12345); myaddr.sin_addr.s_addr = INADDR_ANY; bind( listenfd, (struct sockaddr *)&myaddr, sizeof(myaddr) ); listen( listenfd, 5 ); for( ; ; ) { memset( &cliaddr, 0, sizeof(cliaddr) ); size = sizeof( cliaddr ); connfd = accept( listenfd, (struct sockaddr *)&cliaddr, &size ); writen( connfd, “Alo Mundo”, 10 ); close( connfd ); } return 0; } Preencher a estrutura do endereço local ( myaddr ) para associação com o socket:  sin_addr contém o endereço IP pelo qual o processo deseja receber pacotes. Como uma máquina pode ter vários IPs, é mais prático especificar que se deseja receber pacotes para qualquer um deles. Para isso usamos a constante INADDR_ANY. Exemplo – Um Servidor TCP Associar o endereço preenchido com o socket, para informar o S.O. de que o processo deseja receber pacotes para este endereço. Aguarda um pedido de conexão. Envia uma mensagem de 10 bytes pelo socket. Fecha o socket da conexão. Volta a aceitar novos pedidos. Colocar o socket em modo de escuta. Isto cria uma fila de pedidos de conexão para o socket.

29 Servidores Concorrentes Muitas vezes é necessário para um servidor lidar com vários clientes de uma única vez. Para conseguir isto é preciso, de alguma maneira, voltar a aceitar conexões, sem esperar que um cliente seja completamente servido. Isto normalmente é feito através da criação de novas threads ou novos processos. Um servidor, após o retorno da função accept(), se divide em dois, e enquanto uma linha de execução se dedica a atender o cliente, outra volta a esperar por novos pedidos de conexão.

30 Servidores concorrentes Esquema de um Servidor Típico pid_t pid; int listenfd, confd; listenfd = socket(...); bind(listenfd,...); listen(listenfd, LISTENQ); for( ; ; ) { connfd = accept(listenfd,...); if ( ( pid = fork() ) == 0 ) { close(listenfd); doit(connfd); close(connfd); exit(0) } close (connfd); }

31 Conclusões sobre sockets TCP Um par de aplicações que se comunicam por TCP em geral tem a seguinte forma: accept( ) close( ) connect( ) socket( ) bind( ) listen( ) close( ) Troca de Dados Estabelecimento da Conexão TCP Cliente Servidor Servidores Concorrentes

32 Sockets UDP Sockets sem conexão e sem garantias de entrega ou ordenação. Só é preciso saber o endereço de destino antes de enviar dados. É possível receber dados a qualquer momento e de qualquer um. Usamos as funções sendto() e recvfrom(). Pode-se ainda usar a função connect() com sockets UDP, mas o seu significado aqui é diferente, uma vez que NÃO faz sentido falar em uma conexão UDP. Para sockets UDP, a função connect() apenas “fixa” o endereço conectado. Assim é possível usar as funções send() e recv() como para sockets TCP.

33 Sockets UDP – sendto() Envia dados por um socket NÃO conectado. Parâmetros: – sockfd – o descritor do socket. – buffer – um ponteiro para os dados a serem enviados. – n_bytes – quantidade de bytes a serem enviados. – flags – opções para essa operação. – to – endereço de destino dos dados. – addrlen – tamanho em bytes do endereço de destino. O valor de retorno da função é a quantidade de bytes enviados em caso de sucesso ou –1 caso contrário. #include int sendto( int sockfd, void * buffer, size_t n_bytes, int flags, const struct sockaddr * to, socklen_t addrlen );

34 Sockets UDP – recvfrom() Recebe dados por um descritor NÃO conectado, ou bloqueia a execução até que algum dado chegue: Parâmetros: – sockfd – o descritor do socket. – buffer – um ponteiro para a área de memória onde devem ser armazenados os dados recebidos. – n_bytes – quantidade máxima de bytes a serem recebidos. – flags – opções para essa operação. – from – ponteiro para a estrutura onde será escrito o endereço de origem. – addrlen – argumento valor/resultado com o tamanho do endereço de origem. O valor de retorno da função é a quantidade de bytes recebidos em caso de sucesso ou –1 caso contrário. #include int recvfrom( int sockfd, void * buffer, size_t n_bytes, int flags, struct sockaddr * from, socklen_t * addrlen );

35 #include int main( int argc, char ** argv ) { int sockfd; struct sockaddr_in servaddr; if( argc != 2 ) return –1; sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); memset( &servaddr, 0, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(12345); inet_aton( argv[1], &servaddr.sin_addr ); sendto( sockfd, “Alo Servidor”, 13, MSG_WAITALL, (struct sockaddr *)&servaddr, sizeof(servaddr) ); close( sockfd ); return 0; } Exemplo – Um Cliente UDP Inicializar a estrutura servaddr com zeros. Preencher os campos da estrutura servaddr :  sin_family com o tipo de endereço (AF_INET)  sin_port com a porta (12345) no byte order da rede.  sin_addr com o endereço passado como argumento para o programa. Envia 13 bytes de dados para o servidor. Criar um socket para rede TCP/IP ( AF_INET ), orientado a datagramas ( SOCK_DGRAM ), usando UDP (o protocolo padrão dado por 0 ).

36 #include int main( int argc, char ** argv ) { int sockfd, size, n; struct sockaddr_in myaddr; sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); memset( &myaddr, 0, sizeof(myaddr) ); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(12345); myaddr.sin_addr.s_addr = INADDR_ANY; bind( sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr) ); for( ; ; ) { recvfrom( sockfd, recvline, 30, MSG_WAITALL, NULL, NULL ); fputs( recvline, stdout ); } return 0; } Exemplo – Um Servidor UDP Associar o endereço preenchido com o socket, para informar o S.O. de que o processo deseja receber pacotes para este endereço. Aguarda até 30 bytes. É possível para esta função retornar antes se o outro lado enviar um datagrama menor. Imprime os dados recebidos na saída padrão.

37 Conclusões sobre sockets UDP Um par de aplicações que se comunicam por UDP em geral tem a seguinte forma: close( ) socket( ) bind( ) close( ) Troca de Dados Cliente Servidor bind( )

38 Esta é a forma mais simples de depurar aplicações que envolvem sockets, threads e mais de um processo. Atenção! Deve-se gerar as mensagens de aviso em ‘stderr’ (que não possui buffer) e não ‘stdout’! Inconveniente desta técnica de depuração: pode afetar o funcionamento do programa. Depurando: Passo 0, Exibindo mensagens de aviso

39 Depurando: Passo 1, A Máquina de Estados Comando chave: netstat -a

40 [sadoc@copa src]$ netstat -a | grep 32568 Proto Local Address Foreign Address State tcp *:32568 *:* LISTEN tcp localhost:47415 localhost:32568 ESTABLISHED tcp localhost:32568 localhost:47415 ESTABLISHED [sadoc@copa src]$ netstat -a | grep 32568 tcp localhost:47415 localhost:32568 CLOSE_WAIT tcp localhost:32568 localhost:47415 FIN_WAIT2 Depurando: Passo 1, A Máquina de Estados do TCP Comando chave: netstat -a wildcard

41 tcpdump -x src host_name > log_file Programa equivalente, com interface gráfica: ethereal Depurando: Passo 2, O comando tcpdump Imprime o conteúdo de cada pacote, em hexadecimal Fonte dos pacotes

42 Lições Aprendidas

43 Bibliografia Recomendada Stevens, W. R. – UNIX Network Programming: Volume I – Networking APIs: Sockets and XTI Stevens, W.R. – TCP/IP Illustrated vol.1 Besaw, L. – BSD Sockets Reference (formato PostScript (.ps) em www.land.ufrj.br/~classes/tp/index-103.html).


Carregar ppt "Introdução à Programação com Sockets Fernando Jorge Silveira Filho Daniel Sadoc Menasché"

Apresentações semelhantes


Anúncios Google