comunicacao_sockets

Propaganda
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
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
da máquina */
char sin_zero[8]; /* não usado */
};
struct in_addr {
u_long s_addr; /* end. 32 bit da máquina */
};
bit
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*/
}
Serviço Orientado a Conexão
SERVIDOR
CLIENTE
Cria o descritor:
socket()
para receber pedidos
cria o descritor:
socket()
Atribui um endere₤o
ao descritor:bind()
Atribui ao descritor
um endere₤o
(opcional) :bind()
Avisa que está aceitando
conexões: listen()
Determina o end. servidor
Troca de msg
bloquea/espera
novas conexões.:
accept()(novos
sockets criados na volta)
Conectar o servidor
via socket: connect()
envia msg: send()
espera pelo pkt:recv()
envia resposta:send()
Libera o descritor:
close()
resposta
espera a resp:recv()
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ão bloqueia 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.

*fromaddrptr estrutura 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 e conversões
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 inteiros
para/de 16 e 32-bits de/para “Network Byte Order''
números inteiros devem ser convertido explicitamente para/de
Network Byte Order.
computadores diferentes podem armazenar os bytes de inteiros em uma
ordem diferente na memória.
Network Byte Order é dita big-endian
Big Endian/Little 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 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
SERVIDOR
1.cria o descritor:
socket()
2. atribui ao descritor
um endereço:
bind()
3. Aguarda pkt
chegar: recvfrom()
4. Envia resposta(se houver):
sendto()
5. Libera o descritor:
close()
CLIENTE
1. cria o descritor:
socket()
2. Atribui ao descritor
um endereço:
(opcional) bind()
3. determina endereço
do servidor
4. envia msg: sendto()
5. Aguarda chegada
do pkt : recvfrom()
6. 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, (struct sockaddr *) &my_addr,
sizeof(my_addr)) < 0) ){
printf("Servidor: falha no binding:
%d\n",errno);
exit(0);
}
printf("Servidor: iniciando bloqueio de mensagem
lida\n");
nread = recvfrom(sockid,msg,11,0,
(struct sockaddr *) &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, (struct sockaddr *) &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);
}
Resumo do Fluxograma
TCP / UDP
Uma aplicação de tempo simples:
Cliente:
conecta ao servidor
envia ao servidor a sua hora local
lê de volta a hora do servidor



Servidor:
 recebe conexões dos clientes
 imprime a hora local do cliente
 envia ao cliente a sua hora local
Uma aplicação de tempo simples: código
cliente
1
2
3
4
5
6
7
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
#define SERV_HOST_ADDR "128.119.40.186"
/* Don's host machine */
8 main()
9 {
10 int sockid;
11 struct sockaddr_in ssock_addr;
12 struct timeval tp;
13 struct timezone tzp;
15
16
/* create a socket */
if ((sockid = socket(AF_INET,SOCK_STREAM,
0)) < 0){
17
printf("erro criacao=%d",errno);
18
exit(0);
19 }
20
21 printf("Criando struct addr p/ server…");
22 bzero((char *) &server_addr,
sizeof(server_addr));
23 server_addr.sin_family = AF_INET;
24 server_addr.sin_addr.s_addr =
inet_addr(SERV_HOST_ADDR);
25 server_addr.sin_port =
htons(SERVER_PORT_ID);
26 if (connect(sockid, (struct sockaddr *)
&server_addr, sizeof(server_addr)) <
0){
27
printf("error connecting to server,
error: %d \n",errno);
28
exit(0);
30
/* send time of day */
31
gettimeofday(&tp,&tzp);
32
/* convert from host byte order to
network
byte order */
33
printf("client: local time is %ld
\n",tp.tv_sec);
34
tp.tv_sec = htonl(tp.tv_sec);
35
tp.tv_usec = htonl(tp.tv_usec);
38
/* send time of day to other side */
39
write(sockid, &tp, sizeof(tp));
40
/* get time of day back fro other
side and display */
41
if ( (read(sockid, &tp, sizeof(tp)))
< 0){
42
printf("error reading new socket
\n");
43
exit(0);
44
}
45
46 /* convert from network byte order to
host byte order */
47 tp.tv_sec = ntohl(tp.tv_sec);
48 tp.tv_usec = ntohl(tp.tv_usec);
49 printf("client: remote time is %ld
\n",tp.tv_sec);
50 close(sockid);
51 }
Aplicação de tempo: código servidor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
#define MY_PORT_ID 6090
main()
{
int sockid, newsockid, i,j;
struct sockaddr_in ssock_addr;
struct timeval tp;
struct timezone tzp;
17
18
19
20
21
22
23
24
25
26
29
30
31
if ( (sockid = socket (AF_INET,
SOCK_STREAM, 0)) < 0)
{ printf("erro criacao= %d \n",errno);
exit(0);} /* man errno */
/* name the socket using wildcards */
bzero((char *) &ssock_addr,
sizeof(ssock_addr));
ssock_addr.sin_family = AF_INET;
ssock_addr.sin_addr.s_addr =
htonl(INADDR_ANY);
ssock_addr.sin_port = htons(MY_PORT_ID);
/* bind the socket to port address */
if ( ( bind(sockid, (struct sockaddr *)
&ssock_addr,sizeof(ssock_addr))< 0))
{ printf("error binding socket, error:
%d \n",errno); exit(0); }
/* start accepting connections */
if ( listen (sockid, 5) < 0)
{ printf("erro listening: %d \n",errno);
exit(0); }
33
34
35
36
37
38
39
40
41
42
43
44
for (i=1; i<=50000 ;i++) {
/* accept a connection */
newsockid = accept(sockid, (struct
sockaddr *)0, (int *)0);
if (newsockid < 0)
{ printf("error accepting socket,
error: %d \n",errno); exit(0);
}
/* le tempo remoto */
if ( (read(newsockid, &tp,
sizeof(tp))) < 0)
{ printf("error reading new socket
\n"); exit(0); }
/* convert from network byte order to
host byte order */
tp.tv_sec = ntohl(tp.tv_sec);
tp.tv_usec = ntohl(tp.tv_usec);
printf("server: remote time is
%ld \n",tp.tv_sec);
46 /* get local time of day and send to
client*/
47 for (j=0; j<1000000; j++);
48 /* delay */
49 gettimeofday(&tp,&tzp);
50 /* convert from host byte order to
network byte order */
51 printf("server: local time is
%ld \n",tp.tv_sec);
52 tp.tv_sec = htonl(tp.tv_sec);
53 tp.tv_usec = htonl(tp.tv_usec);
54 write(newsockid, &tp, sizeof(tp));
55 close(newsockid);
56 }
57 close(sockid);
58 }
Bibliografia
 COMER, Douglas E. InternetWorking with TCP/IP -VOL
1.
Tutorial sobre sockets: http://beej.us/guide/bgnet/
http://www.newdevices.com/tutoriales/index.html
Sockets em Java:
 http://www.javasoft.com/docs/books/tutorial/networking/sockets/d
efinition.html
Download