Tema 7
Tema 7
Distribuidos
Tema 4
Tema 4 COMUNICACIÓN CON SOCKETS
. CONCEPTOS
1 BASICOS SOBRE SOCKETS
INTRODUCCIÓN
Mecanismo de IPC que proporciona comunicación entre procesos
que ejecutan en máquinas distintas.
Actualmente disponible en practicamente todos los sistemas operativos y en Java como clase nativa
AF-UNIX :
comunicación dentro de una máquina
AFINET : Comunicación usando protocolos TCP/IP (IPvY)
AF INET6
-
:
comunicación usando protocolos TCP/IP (IPVG)
T I POS DE SOCRETS
=>
RAW (SOCK-RAW sockets sin protocolo de transporte
=>
STREAM (SOCK-STREAM)
Orientado a conexión
↓ Debe establecerse una conexión extremo-a-extremo antes del envío y recepción de datos
↓ de el limite
Flujo bytes (no preserva entre mensajes
s
ordenados por duplicación de de
proporciona fiabilidad paquetes:
secuencia, sin paquetes
, libre errores ,
notifica errores.
= DATAGRAMA (SOCK -
DGRAM)
Protocolo UDP Flujo de datos bidireccional
No orientado a conexión
↓
No se establece/mantiene una conexión entre los procesos que comunican
~
Un
datagrama es una entidad autocontenida : se preservan los limites entre mensajes
~
cabeceral) 64kB. Cabecera IP
Longitud máxima de un
datagrama (datos y es + cabecera UDP =
28 bytes
~
Mantiene separación entre paquetes
J
No proporcionan fiabilidad :
paquetes desadenados, duplicados, pérdidas
EJEMPLOS/Protocolo que usa UDP : ANS
computada destino
2 puertos posibles
en una
máquina -65536 puertos
>
-
: TCP UDP
,
remoto (destino
in_addr
#include <netinet/in.h>
typedef unit32_t in_addr_t;
struct in_addr {
in_addr_t s_addr; //entero sin signo de 32 bits
}
#include <sys/un.h>
struct sockaddr_un {
short sun_family;
char sun_path[108];
}
int main () {
char maquina [256];
int err;
err = gethostname (maquina, 256);
if (err != -1) {
printf("Ejecuto en la maquina %s\n", maquina);
}
exit(0);
}
=> TRANSFORMAR DIRECCIONES
= ) IPV4
=
Notación decimal-punto EJ/ 138 100 8 100
.
. .
DEVUELVE una dirección IP en notación decimal- punto char *inet_ntoa (struct in_addr in);
OBTIENE una dirección /P (binarias a partir de notación decimal-punto
int inet_aton (const char *cp, struct in_addr *in);
Obtiene la infamación de un host a partir de una dirección en
famato dominio -punto (getador info para [PvYe[PvG)
struct hostent *gethostbyname (char *str);
NOMBRE DE LA MÁQUINA
= IPv4 [PVG
,
DEVUELVE una dirección IP en notación entendible (decimal-punto, dirección hexadecimal separada por )
::
const char *inet_ntop (int domain, const void *addrptr, char *dst_str, size_t len)
4
OBTIENE una dirección IP (binaria) a partir de notación entendible
int inet_pton (int domain, const char *src_str, void *addrptr);
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
if (argc != 2) {
printf("Uso: %s <decimal-punto> \n", argv[0]);
exit(0);
}
if (inet_aton (argv[1], &in) == 0) {
printf("Error en la dirección\n");
exit(0);
}
exit(0);
}
ESTRUCTURA Struct hostent
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_lenght;
char **h_addr_list;
}
EJEMPLO/ Conversión dominio-punto a decimal-punto
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
hp = gethostbyname("www.uc3m.es");
if (hp == NULL) {
printf("Error en gethostbyname\n");
exit(0);
}
memcpy (&in.s_addr, *(hp->h_addr_list), sizeof(in.s_addr));
printf("%s es %s\n", hp->h_name, inet_ntoa(in));
return(0);
}
EJEMPLO/ Conversión decimal-punto a dominio-punto
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
if (argc != ") {
printf("USO: %s Direccion-IP\n", argv[0]);
return(1);
}
err = inet_aton(argv[1], &addr);
if (err == 0) {
printf("Dirección IP en formato a.b.c.d\n");
return(2);
}
if (hp == NULL) {
printf("Error en ... \n");
return(3);
}
ORDEN DE
LOS BYTES
LITTLE-ENDIAN
~ de datos (marshalling (
Empaquetamiento
Serialización de las estructuras de datos y conversión de los valores
m
Desempaquetamiento de datos
(unmarshaling
Conversión de los datos a su representación interna
HOST (Little -
Endian) - NETWORK
(Big-Endian (
Para traducir un número de 32/16 bits representado en el
famato del computador al
famato red(TCP/IP) :
#
include < arpa/inet. h/
2 MODELOS
.
DE COMUNICACIÓN
SOCKETS STREAM (SOCK-STREAM SOCRETS DATAGRAMA (SOCK-DGRAM
Orientado a conexión No orientado a conexión
TCP UDP
. API DE
3 PROGRAMACIÓN
CREACIÓN DE UN SOCKET (SOCKET
SOCK -
STREAM ,
SOCKDGRAM letc/protocols
AFUMIX AF-INETS
,
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
int main () {
//Variables del programa
int sockfd;
#include <sys/socket.h>
int bid (int sd, const struct sockaddr *addr, socklen_t addrlen)
Y
1 si error o O si éxito
:
...
=> Si no se
asigna dirección al socket (típico en clientes)
Se le
asigna automáticamente (puerto
efímero) en la primera utilización (connect o
sendto)
#include <sys/types.h>
#include <sys/socket.h>
int listen (int sd, int baklog)
DESCRIPTOR DEVUELTO POR SOCRET ~ NÚMERO MÁXIMO DE PETICIONES QUE SE ENCOLARÁN
MÁXIMO DEFINIDO EN SOMAXCONN (sys/socket h)( .
=> BLOQUEA al servida hasta que se produce la conexión ; el servida obtiene Nuevo descriptor que queda
=> conectado al socket del cliente
Después de la conexión quedan activos 2 sockets en el servidor :
4 El
original para seguir aceptando nuevas conexiones
int accept (int sd, struct sockaddr *dir, socklen_t *len) PARÁMETRO <
2) Después de la llamada tamaño :
de la
DESCRIPTOR DEVUELTO POR SOCRÊT DIRECCIÓN DEL SOCKET DEL CLIENTE VALOR-RESULTADO dirección del cliente que se devuelve en dir
#include <sys/socket.h>
LONGITUD DE LA DIRECCIÓN DEL SOCKET REMOTO
int connect (int sd, const struct sockaddr *dir, socklen_t len)
DESCRIPTOR DEVUELTO POR SOCRET DIRECCIÓN DEL SOCKET REMOTO
=> Devuelve -1 si error o O si éxito
CLIENTES
El cliente que establece una conexión con un host y puerto pasado por parámetro
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
int main() {
int sd, newsd;
socklen_t size;
struct sockaddr_in server_addr, client_addr;
//bind
if (bind(sd, (struct sockaddr *)&server_addr, sizeof(server_addr))<0) {
printf("Error en el bind\n");
return(-1);
}
listen(sd, SOMAXCONN);
for ( ; ; ) {
newsd = accept (sd, (struct sockaddr *) &client_addr, &size);
if (newsd < 0) {
printf("Error en el accept);
return(-1);
}
//transferir datos sobre newsd
//procesar la petición utilizando newsd
close(newsd);
}
close(sd);
}
//La estructura client_addr almacena la IP y el puerto del proceso cliente que se conecta con el servidor
printf("conexion aceptada de IP: %s Puerto: %d\n", inet_ntoa (client_addr.sin_addr, ntohs(client_addr.sin_port));
#include <unistd.h>
ssize_t write (int sd, const char *mem, size_t long)
Pueden no transferirse todos los datos. Es importante comprobar siempre el valor que devuelven estas llamadas
La recepción es una llamada bloqueante (sincrona
RECEPCIÓN :
#include <unistd.h>
int read (int sd, char *mem, int long)
>
Devuelve el número de bytes recibidos
-
0 -1 si error
>
-
Pueden no transferirse todos los datos . Es importante comprobar siempre el valor que devuelven estas llamadas
EJEMPLO/ Función que envía un
bloque de datos con reintentos
if (r<0) {
return(-1); //fallo
} else {
return(0); //se ha enviado longitud
}
}
if (r<0) {
return (-1); //fallo
} else {
return (0); //se ha recibido longitud de bytes
}
}
TRANSFERENCIA DE DATOS CON DATAGRAMAS
conexion
No
hay a Crearlo (socket)
Para usar un socket para transferir datos basta con >
Asignarle una dirección (bind). Si no se le asigna ,
lo
ENVIO :
hará el sistema de manera transparente
#include <sys/types.h>
#include <sys/socket.h> OPCIONESDE O GENERALMENTE
ENVIO ,
int sendto(int sd, const void *buffer, size_t len, int flags, const struct sockaddr *dir, socklent_t addrlen)
ENVIAR
V
RECEPCIÓN :
#include <sys/socket.h>
↑
GENERALMENTE O
int recvfrom(int sd, void *buffer, size_t len, int flags, struct sockaddr *dir, socklent_t addrlen)
L V
DESCRIPTOR DEL SOCRET
LONGITUD
V
Se usa close para cerrar ambos tipos de sockets (stream y datagrama) Devuelve -1 si error
SHUTRD :
cierra el canal de Lectura. Posteriores Lecturas devuelve 0 Se
int shutdown (int sd, int modo);
.
de escritura. Cuando el
.
socket
extremo de la comunicación
SHUT RDWR -
:
cierra ambos canales.
CONFIGURACIÓN DE OPCIONES
IPPROTO_TCP ·
nivel de protocolo TCP
IPPTOTO_IP :
nivel de protocolo IP
=>
CONSULTAR opciones asociadas a un socket
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt (int sd, int nivel, int opcion, void *val, socklen_t *len)
=> MODIFICAR opciones asociadas a un socket int setsockopt (int sd, int nivel, int opcion, const void *val, socklen_t len)
asociadas por si
permanecen todavía segmentos en tránsito en la red
int val = 1;
setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
SO - RCUBUF , SO SNDBUF
_
int size;
err = getsockopt (s, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof(size));
printf("%d\n", size);
TCP-NODELAY
El
Código de operación es un byte
Los enteros son de 32 bits y se envían en el famato de red PROTOCOLO
while (1) {
printf("esperando conexion\n");
sc = accept (sd, (struct sockaddr *)&client_addr, (socklen_t *)&size);
#include <stdio.h>
#include <netdb.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
a = htonl(5);
b = htonl(2);
op = 0; //suma
res = ntohl(res);
printf("2+5 = %d\n", res);
close(s);
}
=>
Comprobación de errores. Muy IMPORTANTE
=>
Aspectos a tener en cuenta en la transferencia de los datos multibyte :
¿ Qué ocurre si el cliente es little-endían y el servidor big-endian ? Hay que resolver el problema de la representación
de los datos. Una posibilidad las
utilizar funciones
:
es
DEFINIR el
formato de representación e intercambio de datos. EJEMPLO :
>
Los datos que se envían a la red deben estar en Netwak Byte Order
Los datos que se reciben de la red deben estar en Host Byte Order
MODELO DE SERVIDOR CONCURRENTE
>
-
SERVIDORES CONCURRENTES CON SOCKETS STREAM MODELO DE COMUNICACIÓN CON SOCKETS DATAGRAMA
La
función que ejecuta el proceso ligero es :
~ La solución es INCORRECTA ya que los procesos padre e hijo compiten por el acceso al descriptor (sd) devuelto
por accept
4 El proceso ligero hijo creado podría usar sd en tratar-petición mientras que un nuevo sa
es
asignado en accept
Y Condiciones de carrera
solución :
desarrollar aplicaciones que codifiquen los datos en cadenas de caracteres y envien cadenas de caracteres
En este caso hay que leer byte byte hasta Leer el código ASCII 'IO'
:
a
buf = buffer;
totRead = 0;
for ( ; ; ) {
numRead = read (fd, &ch, 1); //read a byte
if (numRead == -1) {
if (errno == EINTR) { //interrupted-> restart read()
continue;
}else {
return(-1); //some other error
} else if (numRead == 0) { //EOF
if (totRead == 0) { //no bytes read; return 0
return(0);
} else {
break;
}
} else { //numRead must be 1 if we get here
if (ch == '\n') {
break;
}
if (ch == '\0') {
break;
}
if (totRead < n-1) { //discard > (n-1) bytes
totRead++;
*buf++ = ch;
}
}
*buf = '\0';
return totRead;
}
}
}
ENVIO DE UNA CADENA DE CARACTERES CON SOCKETS STREAM
Para ENVIAR una cadena : Envia la cadena + el código ASCII '10' que indica el fin de la cadena
char buffer[256];
strcpy (buffer, “Cadena a enviar”);
sendMessage(socket, buffer, strlen(buffer) +1);
SERVIDOR Y CLIENTE DE SUMA ENVIANDO CADENAS DE CARACTERES
Para ERVIAR NÚMERO Envía la cadena + el ASCII 10' que indica el de la cadena
un CADENA
Código fin
:
como
De esta farma se independiza del formato concreto en el que se almacenen los enteros
int n = 1234;
char buffer[256];
sprintf(buffer, “%d”, n);
sendMessage(socket, buffer, strlen(buffer)+1);
Y La
función atoi no comprueba que el buffer a convertir sea realmente un número
La
función strtol sí
que lo hace :
int n;
char buffer[256], *endptr;
recvMessage (socket, buffer, 256);
//convert buffer in number
errno = 0;
char op = 0; //suma
int a = 5;
int b = 6;
int res;
char buffer[256];
//PETICIÓN
sendMessage(socket, &op, 1); //envío operación
sprintf(buffer, "%d", a); //envío a
sendMessage(socket, buffer, strlen(buffer)+1);
sprintf(buffer, "%d", b); //envío b
sendMessage(socket, buffer, strlen(buffer)+1);
//RESPUESTA
readLine(socket, buffer, 256);
res = atoi(buffer); //obtiene res
PROTOCOLO DE SERVIDOR DE SUMA USANDO CADEMAS DE CARACTERES
char op = 0; //suma
int a;
int b;
int res;
char buffer[256];
if (op == 0){
res = a+b;
sprintf(buffer, "%d", res);
sendMessage(socket, buffer, strlen(buffer) +1);
}
4
. GUIA DE DISEÑO DE APLICACIONES CLIENTE-SERVIDOR CON SOCKETS
1) IDENTIFICAR EL CLIENTE Y EL SERVIDOR
> Cliente :
elemento activo, varios
>
Servidor : elemento pasivo
2) PROTOCOLO DEL SERVICIO