0% encontró este documento útil (0 votos)
12 vistas18 páginas

Tema 7

El documento aborda la comunicación entre procesos mediante sockets, que son puntos finales de comunicación en sistemas distribuidos. Se describen los tipos de sockets, como SOCK_STREAM para TCP y SOCK_DGRAM para UDP, así como la asignación de direcciones y puertos. También se presentan ejemplos de código y funciones para manejar direcciones IP y convertir entre diferentes formatos.

Cargado por

elena
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
12 vistas18 páginas

Tema 7

El documento aborda la comunicación entre procesos mediante sockets, que son puntos finales de comunicación en sistemas distribuidos. Se describen los tipos de sockets, como SOCK_STREAM para TCP y SOCK_DGRAM para UDP, así como la asignación de direcciones y puertos. También se presentan ejemplos de código y funciones para manejar direcciones IP y convertir entre diferentes formatos.

Cargado por

elena
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Sistemas

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.

Un SOCKET es un descriptor de un punto final de comunicación (dirección IP


y puerto
Abstracción que :

Representa un extremo de una comunicación bidireccional con una dirección asociada.

Ofrece interfaz de capa de transporte del


·

acceso a la protocolo TCP/IP

Actualmente disponible en practicamente todos los sistemas operativos y en Java como clase nativa

DOMINIOS DE COMUNICACIÓN - Un socket está asociado a un dominio desde su creación


3) Un dominio representa una familia de protocolos Sólo se pueden comunicar sockets del mismo dominio

3) Los de del dominio


independientes
·
Ejemplos de dominios servicios sockets son

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)

Protocolo TCP Flujo de datos bidireccional

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.

EJEMPLOS/ Protocolos que usan TCP : HTTP ,


Telnet , FTP ,
SMTP

= 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

DIRECCIONES DE SOCKETS -Asignar una dirección local a un socket (bind)


Las direcciones se usan para
>
Especificar una dirección remota (connect o sendto
4 Las
direcciones son dependientes del dominio
Direcciones en AF UNIX
_
( struct sockaddr_un C .
Nombre del fichero
Direcciones en AF-INET ( struct sockaddr_in 1 . Cada socket debe tener asignada una dirección única
Dirección de host (32 bits) + puerto (16 bits) +
protocolo
2 Es conversión Llamadas
necesario la de tipos (casting) en las
PUERTOS
Un puerto identifica un
proceso destino en un computador
Los puertos se asocian a
procesos
:
permiten que la transmisión se
dirija a un proceso específico en el

computada destino

Un puerto tiene un único receptor y multiples emisores (excepto multicast)


Toda aplicación que desee enviar y recibir data debe abrir un puerto
Número entero de 16 bits

2 puertos posibles
en una
máquina -65536 puertos

Reservada por la IANA para aplicaciones de Internet :


0-1023 (tambiénLlamadas well-known puertos
Puertos entre 1024 y 49151 son puertos registrados para ser usados por los servicios
Puertos encima de 65535 para uso privado
El espacio de puertos para streams y datagrama es independiente

INFORMACIÓN ASOCIADA A UNA COMUNICACIÓN


>
(origen)
>
(destino)
-

>
-

Protocolo Dirección IP local (origen) Puerto Local Dirección IP remota Puerto


-

: TCP UDP
,
remoto (destino

DIRECCIONES IP ENCAPSULACIÓN DE UN PAQUETE TCP

Una dirección IP se almacena en una estructura de tipo -

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
}

D RECCIONES DE SOCRETS EN AFINET

Estructura struct sockador-in sin_family dominio (AFINET)

=> Debe iniciarse 0


a La estructura tiene 3 campos importantes sin_port puerto
#include <netinet/in.h> sin_addr dirección del host
struct sockaddr_in {
short sin_family;
in_port_t sin_port; //16 bits sin signo
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
DIRECCIONES DE SOCKETS EN AF-UNIX
=>
La estructura struct sockador-un describe la dirección de un socket AFLOCAL o AF-UNIX

#include <sys/un.h>
struct sockaddr_un {
short sun_family;
char sun_path[108];
}

SERVICIOS SOBRE DIRECCIONES


=> OBTENER EL NOMBRE DE UN HOST Función el nombre de la
que facilita máquina (famato dominio punto) en la que se ejecuta :

int gethostname (char *name, size_t namelen)


EJEMPLO/ DEL BUFFER
NOMBRE LONGITUD
L

REFERENCIA AL BUFFER DONDE SE ALMACENA EL


#include <unistd.h>
#include <stdio.h>
#inlcude <stdlib.h>
Obtener el nombre de un host en
famato dominio-punto

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

=> OBTENER LA DIRECCIÓN DE UN HOST

= ) IPV4
=
Notación decimal-punto EJ/ 138 100 8 100
.
. .

Usuarios manejan direcciones de texto Notación


en
fama dominio- punto EJ/ www . ucm . es

2 funciones de transformación de direcciones :

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

Obtiene la información de un host a


partir de una dirección IP(getaddrinfo para [Pv4 e[PVG)
struct hostent *gethostbyaddr (const void *addr, int len, int type);
PUNTERO A STRUCT IN ADDRS - TAMAÑO ESTRUCTURAS AF-INET

= IPv4 [PVG
,

2 funciones de transformación de direcciones

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

EJEMPLO/ Manejo de direcciones

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main (int argc, char **argv){


struct in_addr in;

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);
}

printf("La dirección es %s\n", inet_ntoa(in));

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>

int main (int argc, char **argc) {


struct hostent *hp;
struct in_addr in;

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>

int main (int argc, const char **argc) {


struct in_addr addr;
struct hostent *hp;
struct in_addr in;
char **p;
char **q;

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);
}

hp = gethostbyaddr((char *) &addr, sizeof (addr), AF_INET);

if (hp == NULL) {
printf("Error en ... \n");
return(3);
}

for (p = hp->h_addr_list; *p!=0; p++) {


memcpy(&in.s_addr, *p, sizeof(in.s_addr));
printf("%s es \t%s \n", inet_ntoa(in), hp->h_name);
for (q=hp->h_aliases; *q != 0; q++){
printf("%s\n", *q);f
}
}
return(0);
}
BIG-ENDIAN Es el estándar para el ordenamiento de los bytes usando TCP/IP
TambiénLlamado Network byte order

ORDEN DE
LOS BYTES

LITTLE-ENDIAN

=> REPRESENTACIÓN DE DATOS

~ de datos (marshalling (
Empaquetamiento
Serialización de las estructuras de datos y conversión de los valores

de las datos a su representación externa

m
Desempaquetamiento de datos
(unmarshaling
Conversión de los datos a su representación interna

FUNCIONES DE TRANSFORMACIÓN NETWORK HOST


En computadores que no Utilicen
Big-Endian es necesario emplear funciones para traducir números entre el

formato que utiliza TCP/IP (Big-Endian) y el empleado por el


propio computador (Little -Endian) :

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) :

u_long htonl (u_long hostlong)


u_short htohs (u_short hostshort)

NETWORK (Big-Endian) >


-
HOST (Little Endian (
-

Para traducir un número de 32/16 representado


bits en el
famato red (TCP/IP) al
famato del computador
u_long ntohl (u_long netlong)
u_short ntohs (u_short netshort)

#
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

#include <sys/socket.h> -O en caso general


int socket (int dominio, int tipo, int protocolo); > DEPENDE DEL DOMINIO Y TIPO
Especificado en

SOCK -
STREAM ,
SOCKDGRAM letc/protocols
AFUMIX AF-INETS
,

=> Devuelve un descriptor de socket si éxito o1 si error

=> El socket creado NO tiene dirección asignada

EJEMPLO/ Crear un socket TCP

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>

int main () {
//Variables del programa
int sockfd;

if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {


printf("Error en la creación del socket");
return (-1);
}
//Continuación del programa
return(0);
}
ASIGNACIÓN DE DIRECCIONES (bind)
Asignar dirección a un socket

#include <sys/socket.h>
int bid (int sd, const struct sockaddr *addr, socklen_t addrlen)
Y

DESCRIPTOR DEVUELTO POR SOCRET L DIRECCIÓN A ASIGNAR


~
LONGITUD DE LA DIRECCIÓN
=>
TAMAÑO DE LA ESTRUCTURA SOCRADDR
Devuelve -

1 si error o O si éxito

=> Direcciones en dominio AF-INET (struct sockaddr-in)


dirección Local IP INADDR-ANY de la
HOST : una
elige cualquiera máquina
- :
.

PUERTOS puertos disponibles, de los están reservados . Si Ot sistema


65535 cuales 0 1023
elige uno
-

:
...

=> 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)

OBTENER LA DIRECCIÓN DE UN SOCRET


=> Obtener la dirección a partir del descriptor
int getsockname (int sd, struct sockaddr *dir, socklen_t *len) PARÁMETRO VALOR-RESULTADO

DESCRIPTOR DEVUELTO POR SOCRETI DIRECCIÓN DEL SOCKETEVUELTA

=> Obtener la dirección del socket en el otro extremo de la conexión :

int getpeername (int sd, struct sockaddr *dir, socklen_t *len)


PARÁMETRO VALOR-RESULTADO
? S
DESCRIPTOR DEVUELTO POR SOCRET
DIRECCIÓN DEL SOCRET REMOTO

PREPARAR PARA ACEPTAR COMEXIONES (Listen


=> Habilita un socket para recibir conexiones en el servider TCP

=> Realiza en el servidor stream después del socket bind


y

#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)( .

=> Devuelve -1 si error o O si se ejecutó con éxito


ACEPTAR UNA COMEXIÓN (accept)
=> Realizada en el servidor stream después de socket ,
bind y listen ↑ La dirección del socket del cliente

=> 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

4 El nuevo para enviar/ recibir datos por la conexión establecida

#include <sys/socket.h> <


1) Antes de la Llamada tamaño de dir
:

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

=> Devuelve asociado conexión de


un nuevo descripto de socket a la o 2 en caso error

SOLICITUD DE COMEXIÓN (Connect


=> Realizada en el cliente (TCP) para conectarse al servidor

#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

=> Si el socket no tiene dirección asignada, se le


asigna una directamente

EJEMPLO/ Establecimiento de conexión TCP

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>

int main (int argc, char **argv){


int sd;
short puerto;
struct sockaddr_in server_addr;
struct hostent *hp;
if (argc != 3) {
printf("Uso: cliente <host> <puerto> \n");
return(0);
}
puerto = (short) atoi (argv[2]);

sd = socket (AF_INET, SOCK_STREAM, 0);


bzero((char *)&server_addr, sizeof(server_addr));
hp = gethostbyname (argv[1]);

memcpy (&(server_addr.sin_addr), hp->h_addr, hp->h_length);


server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(puerto);
//se establece la conexión
if (connect(sd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
printf("Error en la conexión\n");
return(0);
}
//se usa sd para enviar o recibir datos del servidor
close(sd);
return(0);
}
SERVIDORES

#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;

if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {


printf("Error en la creación del socket");
return(-1);
}

bzero((char *)&server_addr, sizeof(server_addr));


server_addr.sin_family = AF_INET;
server_addr.sin_port = htons (4200);
server_addr.sin_addr.s_addr = INADDR_ANY;

//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);
}

OBTENER LA IP Y EL PUERTO DEL CLIENTE

newsd = accept (sd, (struct sockaddr *) &client_addr, &size);


if (newsd < 0) {
printf("Error en el accept");
return(-1);
}

//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));

TRANSFERENCIA DE DATOS CON STREAMS


Una vez establecida la conexión usando sockets stream, ambos extremos pueden transferir datos
ENVÍO :
Devuelve el número de bytes enviados (0 , no se envío nada) o 1 en caso de error

#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

>
-

Devuelve O si se ha cenado la conexión en el otro extremo .

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

int senddMessage (int socket, char *buffer, int len){


int r;
int l = len;
do {
r = write(socket, buffer, 1);
l = l-r;
buffer = buffer + r;
}while ((1>0) && (r>=0));

if (r<0) {
return(-1); //fallo
} else {
return(0); //se ha enviado longitud
}
}

EJEMPLO/ Función que recibe un


bloque de datos con reintentos

int recvMessage (int socket, char *buffer, int len) {


int r;
int l = len;
do {
r = read (socket, buffer, 1);
l = l-r;
buffer = buffer + r;
} while ((1>0) && (r>=0));

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

DESCRIPTOR DEL SOCRET


S
PUNTERO A LOS DATOS A LONGDATOS DIRECCIÓN DEL SECRET REMOTO CONGITED DIRECCIÓN
Devuelve el nide bytes enviados o1 si error

RECEPCIÓN :

#include <sys/types.h> OPCIONES RECEPCIÓN (man recufrom)

#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

PUNTERO A LOS DATOS LONG DIRECCIÓN DEL SOCRET REMOTOL DIRECCIÓN


A ENVIAR DATOS

Devuelve el nide bytes recibidos o1 si error

CERRAR UN SOCKET (Close #include <unistd.h> >


-

Cierra la conexión en ambos sentidos


~ int close (int sd); >
-

Se usa close para cerrar ambos tipos de sockets (stream y datagrama) Devuelve -1 si error

~ Se puede cerrar el canal de lectura o escritura

SHUTRD :
cierra el canal de Lectura. Posteriores Lecturas devuelve 0 Se
int shutdown (int sd, int modo);
.

DESCRIPTOR DEVUELTO POR SOCKET


E &
puede seguir escribiendo datos
SHUT-WR :
cierra el canal
en el

de escritura. Cuando el
.
socket

extremo de la comunicación

haya leído todos los datos , read


pendientes devolverá 0

SHUT RDWR -
:
cierra ambos canales.
CONFIGURACIÓN DE OPCIONES

Existen varios niveles dependiendo del protocolo afectado como parámetro


SOL_SOCKET :
opciones independientes del protocolo

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)

EJEMPLO/nivel SOL SOCKET


_
:
SO-REUSEADDR permite reutilizar direcciones

¿ POR QUÉ UTILIZAR SO-REUSEADDR ?

Evitar el error en el Servido TCP :


EADDRINUSE("Address already in use")
TCP mantiene las conexiones bloqueadas durante un cierto tiempo (TIME-WAIT)
La conexión ya ha sido cuada y no puede utilizarse, pero todavía no se han eliminado las tablas internas

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
_

Buffer de envío y recepción int size = 16384;


FIJAR TAMAÑO del de envio err = setsockopt (s, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof(size));
buffer
:

4 CONOCER TAMAÑO del buffer de envio :

int size;
err = getsockopt (s, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof(size));
printf("%d\n", size);
TCP-NODELAY

Envío inmediato (sin intentar agrupar mensajes relativamente juntos en el tiempo


int opcion = 1;
rc = setsockopt (s, IPROTO_TCP, TCP_NODELAY, &option, sizeof(option));

ENVIO DE UN FICHERO ENVIO DE UN FICHERO SIN


COMPARACIÓN DE PROTOCOLOS
CON SOCKETS COPIAS DE MEMORIA (ZERO-COPY

MODELO DE SERVIDOR SECUENCIAL

El servidor sirve las peticiones de fama secuencial

Mientras está atendiendo a un cliente no puede aceptar peticiones de mas clientes

EJEMPLO/ servidor y cliente en TCP

EJEMPLO/ Modelo de comunicación


ADVERTENCIA de
código, no
En los
siguientes fragmentos se comprueban en muchos casos los errores que
devuelven las llamadas al sistema . En una implementación real han de tratarse todos los errores y combrobar
los valores
que devuelven todas las llamadas al sistema.

ASPECTOS A TENER EN CUENTA EN EL DISEÑO


La solución planteada tiene que tener en cuenta la ejecución en
arquitecturas
distintas :
big-endian o little-endian
)
protocolo definido :

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

Antes de enviar un entero se convierte a famato de red :


int32_t n = 8;
n = htonl (n);
Después de recibir un entero se convierte a famato write (sc, &n, sizeof(int32_t));
de host para operar después con el :
int32_t n = 8;
read (sc, &n, sizeof(int32_t));
n = ntohl(n);

EJEMPLO/ Servidor TCP


#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> PROTOCOLO DETALLADO

int main (int argc, char *argv[]) {


struct sockaddr_in server_addr, cliente_addr;
socklen_t size;
int sd, sc;
int val;
char op;
int32_t a, b, res;
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) <0) {
printf("SERVER: Error en el socket");
return(0);
}
val = 1;
setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(int));

bzero ((char *)&srver_addr, sizeof(server_addr));


server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(4200);

bind (sd, (const struct sockaddr *)&server_addr, sizeof(server_addr));


listen (sd, SOMAXCONN);
size = sizeof(client_addr);

while (1) {
printf("esperando conexion\n");
sc = accept (sd, (struct sockaddr *)&client_addr, (socklen_t *)&size);

read (sc, (char *) &op, sizeof(char)); //recibe la operación


read (sc, (char *) &a, sizeof(int32_t)); //recibe a
read (sc, (char *) &b, sizeof(int32_t)); //recibe b

if (op == 0) { //procesa la petición


res = ntohl(a) + ntohl(b);
}else {
res = ntohl(a) - ntohl(b);
}
res = htonl(res);
write (sc, (char *)&res, sizeof(int32_t)); //envía el resultado
close (sc); //cierra la conexión (sc)
}
close(sd);
return(0);
}
EJEMPLO/ Cliente TCP

#include <stdio.h>
#include <netdb.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

int main (int argc, char **argv) { //en argv[1] == servidor


int sd;
struct sockaddr_in server_addr;
struct hostent *hp;
int32_t a, b, res;
char op;
if (argc != 2) {
printf("Uso: cliente <dirección_servidor> \n");
return(0);
}
sd = socket (AF_INET, SOCK_STREAM, 0);

bzero((char *)&server_addr, sizeof(server_addr));


hp = gethostbyname (argv[1]);

memcpy (&(server_addr.sin_addr), hp->h_addr, hp->h_lenght);


server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(4200);

//se establece la conexión


connect (sd, (struct sockaddr *) &server_addr, sizeof(server_addr));

a = htonl(5);
b = htonl(2);
op = 0; //suma

write(sd, (char *)&op, sizeof(char)); //envía la operación


write (sd, (char *) &a, sizeof(int32_t)); //envía a
write (sd, (char *) &b, sizeof(int32_t)); //envía b

read(sd, (char *) &res, sizeof(int32_t)); //recibe la respuesta

printf("Resultado es %d \n", ntohl(res));


close (sd);
return(0);
}
EJEMPLO/ Servidor y cliente en UDP

EJEMPLO/ Modelo de comunicación

EJEMPLO/ Servidor UDP


bind (s, (struct sockaddr *)&server_addr, sizeof(server_addr));
#include <stdio.h> clilen = sizeof(client_addr);
#include <strings.h>
#include <sys/types.h> while(1){
#include <sys/socket.h> recvfrom (s, (char *)peticion, 3*sizeof(int32_t), 0,
#include <arpa/inet.h> (struct sockaddr *)&client_addr, &clilen);
peticion[0]= ntohl(peticion[0]);
int main(void) { peticion[1] = ntohl(peticion[1]);
int32_t peticion[3]; peticion[2] = ntohl(peticion[2]);
int s, res, clilen; if (peticion[0] == 0) {
struct sockaddr_in server_addr, client_addr; res = peticion[1] + peticion[2];
s = socket(AF_INET, SOCK_DGRAM, 0); } else {
res = peticion[1] - peticion[2];
bzero((char *)&server_addr, sizeof(server_addr)); }
server_addr.sin_family = AF_INET; res = htonl(res);
server_addr.sin_addr.s_addr = INADDR_ANY; sendto(s, (char *)&res, sizeof(int32_t), 0,
server_addr.sin_port = htons(7200); (struct sockaddr *)&client_addr, clilen);
}
}
EJEMPLO/ Cliente UDP
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main (int argc, char *argv[]) {


struct sockaddr_in server_addr, client_addr;
struct hostent *hp;
int s;
int32_t peticion[3], res;
if (argc != 2) {
printf("Uso: cliente <direccion_servidor>\n");
return(0);
}

s = socket (AF_INET, SOCK_DGRAM, 0);


hp = gethostbyname (argv[1]);

bzero((char *)&server_addr, sizeof(server_addr));


memcpy (&(server_addr.sin_addr), hp->h_addr, hp->h_length);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7200);

bzero((char *)&client_addr, sizeof(client_addr));


client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = INADDR_ANY;
client_addr.sin_port = htons(0);

bind(s, (struct sockaddr ^)&client_addr, sizeof(client_addr));

peticion[0] = htonl(0); //sumar


peticion[1] = htonl(5);
peticion[2] = htonl(2);

sendto(s, (char *) peticion, 3*sizeof(int32_t), 0, (struct sockaddr *) &server_addr, sizeof(server_addr));

recvfrom(s, (char *)&res, sizeof(int32_t), 0, NULL, NULL);

res = ntohl(res);
printf("2+5 = %d\n", res);
close(s);
}

CONSIDERACIONES A TENER EN CUENTA

=>
Comprobación de errores. Muy IMPORTANTE
=>
Aspectos a tener en cuenta en la transferencia de los datos multibyte :

ORDENAMIENTO de los bytes

¿ 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

u_long htonl (u_long hostlong)


u_long htons (u_short hostshort)
u_long ntohl (u_long netlong)
u_short ntohs (u_short netshort)

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
>
-

El servidor crea un hijo que atiende la petición y envía la respuesta al cliente

-Se pueden atender multiples peticiones de


fama concurrente

SERVIDORES CONCURRENTES CON SOCKETS STREAM MODELO DE COMUNICACIÓN CON SOCKETS DATAGRAMA

SERVIDORES CONCURRENTES CON THREADS

=> PROCESOS CONCURRENTES CON THREADS (CREACIÓN BAJO DEMANDA


E servidor crea un sockets y le asocia una dirección.

El cuerpo principal del servidor es :

La
función que ejecuta el proceso ligero es :

Y SOLUCIÓN INCORRECTA = NECESARIO SINCRONIZACIÓN !

~ 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

Necesario las y variables condicionales


u) sincronizar acciones con mutex

En el proceso servida principal


En los procesos ligera hijos
PROCESO LIGERO PRINCIPAL PROCESO LIGERO HIJO
HETEROGENEIDAD
En
general cuando se desarrolla una aplicación con sockets
hay que plantear una solución que sea independiente de :

Arquitectura (Little Endian Big-Endian) Lenguaje de


programación
-

Emplear soluciones que definan el tamaño de los entera


(EJ/32bits) puede ser un problema

solución :
desarrollar aplicaciones que codifiquen los datos en cadenas de caracteres y envien cadenas de caracteres

Protocolos basados en texto. EJEMPLO/HTTP ,


SMTP

LECTURA DE CADENAS DE CARACTERES CON SOCKETS STREAM

Cuando una cadena de caracteres


finaliza con el código ASCII 'l0'no sabe a priori su
longitud y no se puede usar la

función recuMessage para leerla

En este caso hay que leer byte byte hasta Leer el código ASCII 'IO'
:
a

ssize readLine (int fd, void *buffer, size_t n) {


ssize_t numRead; //num of bytes fetched by last read()
size_t totREad; //total bytes read so far
char *buf;
char ch;

if (n<=0 || buffer == NULL) {


errno = EINVAL;
return(-1);
}

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

Para RECIBIR Un NÚMERO Como CADENA :


int n;
char buffer[256];
readLine(socket, buffer, 256);
n = atoi(buffer);
USO DE STRTOL PARA DETECTAR ERRORES

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;

//To distinguish success/failure after call


n = strtol(str, &endptr, 10); //base 10
//check for various possible errors
if ((errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)) || (errno != 0 && n == 0)) {
perror("strtol");
exit(EXIT_FAILURE);
}
if (endptr == str) {
fprintf(stderr, "No digits were found\n");
exit(EXIT_FAILURE);
}
//If we got here, strtol() successfully parsed a number
printf("strtol() returned %ld\n", n);
PROTOCOLO DEL CLIENTE DE SUMA USANDO CADENAS

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

recvMessage(socket, &op, 1); //obtiene op


//recibe argumentos
readLine(socket, buffer, 256);
a = atoi(buffer); //obtiene a
readLine(socket, buffer, 256);
b = atoi(buffer); //obtiene b

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

Identificar los tipos de mensajes


>
y la secuencia de intercambios de mensajes (peticiones y respuestas)
3) ELEGIR EL TIPO DE SERVIDOR
> UDP sin conexión
> TCP :
una conexión por sesión . Una conexión por petición
4) IDENTIFICAR EL FORMATO DE LOS MENSAJES (REPRESENTACIÓN DE LOS DATOS
>
Independencia (lenguaje, arquitectura, implementación, ) ...

También podría gustarte