630 likes | 792 Views
Comunicação entre processos usando Sockets Disciplina: Linguagem de Programação IV. Revisão da aula anterior. Revisão do programa fork. Quais são os problemas em relação aos processos pesados ?. Comunicação entre processos. send / receive (troca de mensagens) RPC ( Remote Procedure Call )
E N D
Comunicação entre processos usando Sockets Disciplina: Linguagem de Programação IV
Revisão da aula anterior • Revisão do programa fork. • Quais são os problemas em relação aos processos pesados ?
Comunicação entre processos • send / receive (troca de mensagens) • RPC (Remote Procedure Call) • C. 1984, ex.: rpcinfo/portmapper. • Objetos distribuídos. Maior nível de abstração.
API: Application Programming interface • API: Modelo de Programação • disponível em muitos sistemas operacionais • ex: Windows NT, Unix, etc. • Facilita a Programação • Torna as aplicações mais flexíveis
Revisão Sockets • O que é um Socket? • Socket é uma ponta de uma comunicação ponto-a-ponto entre dois programas rodando em uma rede.
A API SOCKET • Introduzido em 1981 no UNIX BSD(Berkeley Software Distribution) 4.1
A API SOCKET - cont. • Cada aplicação conhece apenas o seu próprio Socket; • Os sockets são explicitamente criados, usados e liberados pela própria aplicação; • Baseado no paradigma cliente/servidor; • dois tipos de serviço de transporte via API socket: • datagramas não confiáveis • confiáveis, orientado a conexão
Sockets: visão conceitual • cada socket possui o seu próprio buffer para envio e recepção de dado, número de porta, parâmetro; • As operações com o socket são construídas como chamada a funções do sistema operacional.
Endereçamento na Internet • Cada máquina na Internet possui um ou mais endereços IP 32-bit únicos e globais; • A máquina pode ter 1 ou mais endereços • cada endereço está associado a cada placa de rede • Notação de ponto decimal: • 4 inteiros decimais , cada grupo representando um byte de endereço IP.
Endereçamento na Internet • A função inet_addr() converte da notação ponto decimal para o endereço de 32-bit; • A função gethostbyname() converte o nome textual para o ponto decimal.
DNS: Sistema de nomes de Domínio na Internet • uma base de dados distribuída usada por aplicações TCP/IP para mapear de/para nomes de máquina para/de endereços IP. • servidor de nomes: • as funções de usuário gethostbyname() e gethostbyaddress() contactam o servidor de nome local através da porta 53 • o servidor de nomes retorna um endereço IP do nome da máquina solicitada
Criando um socket • Mesmo ponto comum (socket) usado para enviar/receber dados • não existe, a priori, associação do socket com a rede • deve especificar a família de protocolo, bem como o nível do serviço a ser usado com o socket: Tipo de serviço: Datagrama (SOCK_DGRAM) = UDP Confiável (SOCK_STREAM) = TCP
Criando um socket - cont. int socket (int family, int service, int protocol) • family₫ um nome simbólico para a família de protocolos • service₫ um nome simbólico para o tipo de serviço • Protocol₫ para uso futuro. Esse valor será 0. Obs.: O código de retorno da função socket() é um descritor, usado em todas as chamadas ao socket criado. Exemplo: #include <sys/types.h> #include <sys/socket.h> int sockfd; if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { /* handle error */ }
Atribuindo um endereço de rede ao socket: bind() • Cada socket deve ser associado a uma porta local que deve ser única. • A porta é uma abstração do protocolo TCP/IP para distinguir entre múltiplos destinos dentro de uma determinada máquina • Precisa especificar o endereço de rede • O S.O. sabe que as messagens recebidas naquele endereço e porta devem ser entregues para o socket. • Endereço de portas. Ver arquivos /etc/services.
Endereçamento de Socket: estruturas de endereço pré-definidas Especificando endereços de socket: algumas estruturas de dados usadas na implementação: struct sockaddr_in { short sin_family; /* default AF_INET */ u_short sin_port; /* número de 16 bit */ struct in_addr sin_addr; /* endereço de 32 bit da máquina */ char sin_zero[8]; /* não usado */ }; struct in_addr { u_long s_addr; /* end. 32 bit da máquina */ };
A chamada ao bind() int bind ( int sockfd, struct sockaddr *myaddr, int addresslen) • sockfd: é o número do socket obtido anteriormente. • *myaddr: especifica o end. local associado ao socket(inclusive a porta). • addresslen: é o tamanho da estrutura de endere₤o Obs.: se a função retornar um erro então o número da porta já está em uso ou fora do limite.
A chamada ao bind() - cont. #include <sys/types.h> #include <sys/socket.h> #include <inet.h> int sockfd; struct sockaddr_in myaddr; if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { /* Manipulador de erro */ } myaddr.sin_family = AF_INET; myaddr.sin_port = htons(5100); myaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* INADDR_ANY = SO determina o hostid */ if ( bind(sockfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0) { /* Manipulador de erro*/ }
SERVIDOR CLIENTE Cria o descritor: socket() para receber pedidos cria o descritor: socket() Atribui ao descritor um endere₤o (opcional) :bind() Atribui um endere₤o ao descritor:bind() Avisa que está aceitando conexões: listen() Determina o end. servidor Troca de msg Conectar o servidor via socket: connect() bloquea/espera novas conexões.: accept()(novos sockets criados na volta) Requisita envia msg: send() espera pelo pkt:recv() resposta espera a resp:recv() envia resposta:send() Libera o descritor: close() Libera o descritor: close()
Serviço Orientado a Conexão • aperto de mão cliente/servidor: • cliente deve se conectar ao servidor antes de enviar ou receber dados • cliente não passará do connect() até o servidor aceitá-lo • servidor deve aceitar o cliente antes de enviar ou receber dados • servidor não passará do accept() até o cliente usar connect() • serviço orientado a conexão: serviço confiável oferecido pela camada de transporte.
conexão cliente-para-servidor : connect() • cliente usa connect() para requisitar conexão junto ao servidor • protocolo da camada de transporte (ex: TCP) inicia procedimento para conexão através do aperto de mão cliente/servidor • connect()retorna quando o servidor aceita a conexão ou time-out (não há resposta) • usado com protocolos confiáveis, mas também com datagramas
Conexão cliente-para-servidor : connect() - cont. int connect ( int sockfd, struct sockaddr *toaddrptr, int addresslen) • sockfd:número do socket atribuído anteriormente. Os processos o usam para enviar conexões aceitas. • *toaddrptr:especifica o end. Remoto (inclusive a porta). • Addresslen :é o tamanho da estrutura de endereço.
A chamada listen() • Usado por servidores orientados a conexão. • avisa a rede/S.O. que o servidor aceitará requisições para conexão. • Nãobloqueia e não espera por requisições! • int listen ( int sockfd, int maxwaiting) • sockfd:número do socket atribuído anteriormente. • maxwaiting:número máximo de conexões que podem ser enfileiradas, enquanto aguardam o servidor executar um accept(). O valor típico é 5.
Conexão servidor-para-cliente : accept() • Executado pelo servidor após listen(). • servidor irá aceitar as novas conexões via socket novo, isto é, retornará um novo número de socket para usar na comunicação de volta ao cliente.
Accept() -cont. int accept ( int sockfd, struct sockaddr *fromaddrptr, int *addresslen) • sockfd número do socket atribuído anteriormente. • *fromaddrptrestrutura que contém o endereço do cliente onde enviar as respostas. • Addresslené o tamanho da estrutura de endereço.
Accept() -cont struct sockaddr_in other_app_addr; int sockid, newsockid, addrsize; addrsize = sizeof(other_app_addr)); newsockid = accept(sockid, (struct sockaddr *) &other_app_addr, &addrsize); /* newsockid to communicate with client, sockid to accept more connections */
Enviando e recebendo dados Os dados são enviados/recebidos usando chamadas E/S do Unix ou chamadas de rede • send/recv para sockets • write/read para qualquer operação de I/O
send() int send (int sockfd, char *buff, int bufflen, int flags) • sockfd número do socket . • *buff é o endereço do dado a ser enviado. O conteúdo abriga a mensagem. • bufflen é número de bytes a enviar. • flags controla a transmissão. Para nós será sempre 0 (zero) . Obs.: retorna o número de bytes efetivamente enviado.
Exemplo: usando send() char buffer[50];struct sockaddr_in other_app_addr;int retcode /* suppose we have done socket() and bind() calls, filled in other_app_addr,and put 23 bytes of data into buffer */retcode = send(sockfd, buffer, 23, 0)
recv() int recv (int sockfd, char *buff, int bufflen, int flags) • sockfd é o número do socket obtido anteriormente. • *buff é o endereço onde o dado será armazenado. • bufflen número máximo esperado • flags controla a recepção. Esse valor será sempre 0. Obs.: recv() retorna o número de bytes efetivamente recebidos
Exemplo: usando recv() char buffer[50];struct sockaddr_in other_app_addr;int nread, addrlen; /* suppose we have done socket(), bind() */nread = recv(sockfd, buffer, 23, 0)
Sockets - outras funções • Sockets - estruturas • Sockets - funções • Ordenação de bytes e conversões • Serviço não orientado a conexão
Sockets - estruturas struct sockaddr { // estrutura parâmetro de connect() unsigned short sa_family; // AF_xxx char sa_data[14]; // IP + porta }; • Estrutura paralela para a Internet: struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; // pad };
Sockets - estruturas struct in_addr { unsigned long s_addr; } • sin_port e sin_addr devem estar em Network byte order. (htonx()), isto é, colocar os bytes em ordem de rede antes de mandá-los pela rede. • Motivo: são os campos enviados pela rede.
Ordenação de Bytes • Byte mais significante • chamado de Network byte order (NBO) • BigEndian • Byte menos significante • chamado de Host byte order (HBO) • LittleEndian
Rotinas de Ordenação de Byte • Rotinas de ordenação de bytes: converte bytes de inteirospara/de 16 e 32-bits de/para “Network Byte Order'' • númerosinteirosdevem ser convertidoexplicitamentepara/de Network Byte Order. • computadoresdiferentespodemarmazenaros bytes de inteirosemumaordemdiferentenamemória. • Network Byte Order é dita big-endian
Sockets – funções Se tivermos: struct sockaddr_in ina; • ina.sin_addr.s_addr = inet_addr(“132.241.5.10”); • inet_addr() retorna o endereço em NBO. • printf(´´%s´´, inet_ntoa(ina.sin_addr)); • Seqüência de código para aceitar conexões: • socket(); • bind(); • listen(); • accept();
Procedures do UNIX • htonl converte formato de host de 32 bit para nbo; • ntohl converte nbo para o formato host de 32 bit; • htons converte formato de host de 16 bit para nbo; • ntohs converte nbo para o formato host de 16 bit.
Serviço não orientado a conexão • Serviço de datagrama: o protocolo da camada de transporte não garante a entrega do pacote; • Não há identificação explícita de quem é o servidor e quem é o cliente; • Ao iniciar o contato com o outro lado precisamos saber: • o endereço IP; • número da porta onde contactar o outro lado. • Ao esperar ser contactado pelo outro lado, precisa declarar • número da porta que está esperando o outro lado
CLIENTE 1. cria o descritor: socket() SERVIDOR 1.cria o descritor: socket() 2. Atribui ao descritor um endereço: (opcional) bind() 2. atribui ao descritor um endereço: bind() 3. determina endereço do servidor 4. envia msg: sendto() 3. Aguarda pkt chegar: recvfrom() 5. Aguarda chegada do pkt : recvfrom() 4. Envia resposta(se houver): sendto() 6. Libera o descritor: close() 5. Libera o descritor: close()
Exemplo: Servidor não orientado #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define MY_PORT_ID 6090 /* numero > 5000 */ main() { int sockid, nread, addrlen; struct sockaddr_in my_addr, client_addr; char msg[50];
Exemplo: Servidor não orientado - cont. printf("Servidor: criando o socket\n"); if ( (sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ printf("Servidor: erro no socket: %d\n",errno); exit(0); } printf("Servidor: Bindando socket local\n"); bzero((char *) &my_addr, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = htons(INADDR_ANY); my_addr.sin_port = htons(MY_PORT_ID);
Exemplo: Servidor não orientado- cont. • if ( (bind(sockid, (structsockaddr *) &my_addr, • sizeof(my_addr)) < 0) ){ • printf("Servidor: falha no binding: • %d\n",errno); • exit(0); • } • printf("Servidor: iniciandobloqueio de mensagemlida\n"); • nread = recvfrom(sockid,msg,11,0, • (structsockaddr *) &client_addr, &addrlen); • printf("Servidor: cod retorno lido é %d\n",nread); • if (nread >0) printf("Servidor: mensagem é: • %.11s\n",msg); • close(sockid); • }
Exemplo: Cliente não orientado #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define MY_PORT_ID 6089 #define SERVER_PORT_ID 6090 #define SERV_HOST_ADDR "128.119.40.186" main() { int sockid, retcode; struct sockaddr_in my_addr, server_addr; char msg[12];
Exemplo: Cliente não orientado - cont. • printf("Cliente: criando socket\n"); • if ((sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ • printf("Cliente: falha no socket: %d\n",errno); • exit(0); • } • printf("Cliente: amarrando socket local\n"); • bzero((char *) &my_addr, sizeof(my_addr)); • my_addr.sin_family = AF_INET; • my_addr.sin_addr.s_addr = htonl(INADDR_ANY); • my_addr.sin_port = htons(MY_PORT_ID); • if ( ( bind(sockid, (structsockaddr *) &my_addr, • sizeof(my_addr)) < 0) ){ • printf("Cliente: falha no bind: %d\n",errno); • exit(0); • }
Printf("Cliente: criando estrutura addr para • o servidor\n"); • bzero((char *) &server_addr, sizeof(server_addr)); • server_addr.sin_family = AF_INET; • server_addr.sin_addr.s_addr = • inet_addr(SERV_HOST_ADDR); • server_addr.sin_port = htons(SERVER_PORT_ID); • printf("Cliente: iniciando mensagem e • enviando\n"); • sprintf(msg, “Ola para todos"); • retcode = sendto(sockid,msg,12,0,(struct • sockaddr *)&server_addr, • sizeof(server_addr)); • if (retcode <= -1){ • printf("cliente: falha no sendto: %d\n",errno); • exit(0); • } • /* close socket */ • close(sockid); • }