Está en la página 1de 56

SISTEMAS

OPERATIVOS
COMUNICACIÓN DE PROCESOS A TRAVÉS DE
SOCKETS
Comunicación de procesos a través de
sockets
• Estructura en capas del modelo OSI
• Protocolo TCP/IP
• Comunicación Cliente-Servidor
• Sockets
Estructura en capas del modelo OSI
(Interconexión de Sistemas Abiertos)
Protocolos de comunicación
• Protocolo: conjunto de reglas y formatos que permiten la
comunicación entre procesos.
• La definición de un protocolo tiene dos parte:
• Especificación de la secuencia de mensajes que deben
intercambiarse.
• Especificación del formato de mensajes.
• El software de red se organiza en niveles
Funciones de una pila de protocolos
• Segmentación y ensamblado de mensajes
• Encapsulado: incorporación de información de control a
los datos
• Dirección del emisor y receptor
• Código de detección de errores
• Control de conexión
• Protocolos orientados a conexión
• Protocolos no orientados a conexión:
• No se asegura el orden secuencial de los datos transmitidos

• Entrega ordenada en protocolos orientados a conexión


• Números de secuencia
Funciones de una pila de protocolos II
• Control de flujo: función realizada en el receptor para
limitar la cantidad o tasa de datos que envía el emisor.
• Control de errores: se basan en el uso de una
secuencia de comprobación y reenvío.
• Direccionamiento, conseguir que los mensajes alcancen
al receptor
• Multiplexación: necesario para un uso más eficiente de
los servicios
• Servicios de transmisión:
• Prioridad
• Calidad de servicio
• Seguridad
Protocolo TCP/IP
• Representación de la arquitectura del protocolo TCP/IP
Protocolo Internet (nivel IP)
• La transmisión no es fiable (no se asegura la recepción
de los paquetes IP). Los paquetes se pueden descartar
por:
• Expiración del tiempo de vida
• Congestión
• Error en la suma de comprobación
• Control de flujo muy limitado
• Calidad de servicio muy limitado
• Seguridad: normal o alto
• Retardo: normal o bajo
• Rendimiento: normal o alto
Protocolos de transporte
• Protocolo TCP
• Orientado a conexión
• Garantiza que los datos se entregan en el orden en el que se
envían
• Las conexiones TCP se ven como un flujo de bytes
• La transmisión se considera “fiable”. Pueden perderse mensajes
(sobrecarga en la red, fallos en encaminadores, etc.)
• Cuando los mensajes son muy pequeños, TCP los retrasa hasta
conseguir uno más grande
• Esta opción debe desactivarse si es necesario
• Escrituras concurrentes sobre una misma conexión TCP pueden
provocar que los datos se entremezclen.
Protocolos de transporte
• Protocolo UDP
• Protocolo de datagramas no orientado a conexión.
• Protocolo no fiable
• Los paquetes se pueden perder, duplicar, recibir en orden distinto al
enviado
• Tamaño máximo del mensaje: 64 KB
Encaminamiento
• Permite que los paquetes viajen del proceso emisor al
receptor.
• Algoritmo:
• Un programa de aplicación genera un paquete, o bien se lee un
paquete de la interfaz de red.
• Si el paquete es para la máquina, se acepta.
• En caso contrario, se incrementa el contador de saltos, si se
excede el máximo, el paquete se descarta.
• Si el paquete no es para la máquina se busca en la tabla de
encaminamiento y se retransmite a la interfaz adecuada.
• Tablas estáticas, las más utilizadas
• Tablas dinámicas
Papel del sistema operativo
• El SW de comunicación de un sistema operativo se
organiza como un conjunto de componentes con tareas
concretas
• Subsistema de almacenamiento: buffers donde almacenar los
paquetes que llegan y se envían (limitado)
• En implementaciones UNIX típicas
• TCP reserva para cada puerto (socket) un buffer de 8 KB y UDP 2
buffers de 8KB. El tamaño se puede incrementar hasta 64 KB.
• Los mensajes a enviar se copian a estos buffers
• El contenido de estos buffers se fragmenta y se copian a nuevos
bloques de memoria a utilizar por IP
• IP envía finalmente los paquetes por la interfaz de red
correspondiente
Papel del sistema operativo
• Un sistema operativo puede perder paquetes cuando la
tasa de envíos y de recepción es muy grande.
• En sistemas operativos multiusuario la pérdida de
paquetes suele producirse a ráfagas debido a los
algoritmos de planificación.
40

• La latencia del SO 35

30
ha crecido en términos 25
relativos 20

15

10

5
0
1985-1990 1990-1995 1995-2000 2000-20005
¿Dónde se pierde el tiempo?
• Códigos de corrección (Checksum)
• Sobre datos TCP y UDP
• Sobre cabeceras IP
• Copias de datos
• Entre usuario y kernel
• Estructura de datos
• Gestión de los buffers, colas de defragmentación de paquetes IP,
• Sistema Operativo
• Sobrecarga impuesta por las operaciones del SO
Sistema operativo en red (SOR)
Aplicaciones Aplicaciones
Lenguajes de programación Lenguajes de programación
Sistema operativo Sistema operativo
Hardware Hardware

Red de interconexión

• El usuario ve un conjunto de máquinas independientes


• No hay transparencia
• Se debe acceder de forma explícita a los recursos de
otras máquinas
• Difíciles de utilizar para desarrollar aplicaciones
distribuidas
Sistema operativo distribuido (SOD)
Aplicaciones
Lenguajes de programación
Sistema operativo distribuido
Hardware Hardware

Red de interconexión

• Se comporta como un SO único (visión única)


• Distribución. Transparencia
• Se construyen normalmente como micronúcleos que
ofrecen servicios básicos de comunicación
• Mach, Amoeba, Chorus.
• Todos los computadores deben ejecutar el mismo SOD
Comunicación Cliente-Servidor
• Protocolo típico: Petición-Respuesta
Sockets
• Aparecieron en 1981 en UNIX BSD 4.2
• Intento de incluir TCP/IP en UNIX
• Diseño independiente del protocolo de comunicación
• Un socket es punto final de comunicación (dirección IP y
puerto)
• Abstracción que:
• Ofrece interfaz de acceso a los servicios de red en el nivel de
transporte
• Protocolo TCP
• Protocolo UDP
• Representa un extremo de una comunicación bidireccional con una
dirección asociada
Sockets: introducción
• Sujetos a proceso de estandarización dentro de POSIX
(POSIX 1003.1g)
• Actualmente
• Disponibles en casi todos los sistemas UNIX
• En prácticamente todos los sistemas operativos
• WinSock: API de sockets de Windows
• En Java como clase nativa
Sockets UNIX
• Dominios de comunicación
• Tipos de sockets
• Direcciones de sockets
• Creación de un socket
• Asignación de direcciones
• Solicitud de conexión
• Preparar para aceptar conexiones
• Aceptar una conexión
• Transferencia de datos
Dominios de comunicación
• Un dominio representa una familia de protocolos
• Un socket está asociado a un dominio desde su creación
• Sólo se pueden comunicar sockets del mismo dominio
• Algunos ejemplos:
• PF_UNIX (o PF_LOCAL): comunicación dentro de una máquina
• PF_INET: comunicación usando protocolos TCP/IP
• Los servicios de sockets son independientes del dominio
Tipos de sockets
• Stream (SOCK_STREAM)
• Orientado a conexión
• Fiable, se asegura el orden de entrega de mensajes
• No mantiene separación entre mensajes
• Si PF_INET se corresponde con el protocolo TCP
• Datagrama (SOCK_DGRAM)
• Sin conexión
• No fiable, no se asegura el orden en la entrega
• Mantiene la separación entre mensajes
• Si PF_INET se corresponde con el protocolo UDP
• Raw (SOCK_RAW)
• Permite el acceso a los protocolos internos como IP
Direcciones de sockets
• Cada socket debe tener asignada una dirección única
• Las direcciones se usan para:
• Asignar una dirección local a un socket (bind)
• Especificar una dirección remota (connect o sendto)
• Dependientes del dominio
• Se utiliza la estructura genérica struct sockaddr
• Cada dominio usa una estructura específica
• Direcciones en PF_UNIX (struct sockaddr_un)
• Nombre de fichero
• Direcciones en PF_UNIX (struct sockaddr_in)
• Uso de conversión de tipos (casting) en las llamadas
Direcciones de sockets en PF_INET
• Host (32 bits) + puerto (16 bits)
• Una dirección IP se almacena en una estructura de tipo:
• struct in_addr
• Estructura struct sockaddr_in
• Debe iniciarse a 0
• sin_family: dominio (AF_INET)
• sin_port: puerto
• sin_addr: dirección del host
• Función que facilita el nombre de la máquina en la que se
ejecuta:
int gethostname(char *name, int namelen);
Obtención de la dirección de host
• Usuarios manejan direcciones en forma de texto:
• decimal-punto: 138.100.8.100
• dominio-punto: laurel.datsi.fi.upm.es

• Algunas funciones para trabajar con direcciones:


• char *inet_ntoa(struct in_addr in);
• Devuelve una dirección en notación decimal-punto.
• struct hostent *gethostbyname(char *str);
• Convierte una dirección en notación dominio-punto a una estructura que
describe máquina.
• Algunos campos de la estructura struct hostent:
• char *name nombre oficial de la máquina
• char **h_addr_list lista de direcciones
Ejemplo
• Programa que obtiene la dirección en formato decimal-punto
a partir de un formato dominio-punto.
void main(int argc, char **argv) {
struct hostent *hp;
struct in_addr in;

hp = gethostbyname(argv[1]);
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));
}
Direcciones de sockets II
• En TCP/IP los números se emplean con formato big-
endian.
• En computadores que no utilicen este formato es
necesario emplear funciones para traducir números entre
el formato que utiliza TCP/IP y el empleado por el propio
computador:
u_long htonl (u_long hostlong)
u_short htons (u_short hostshort)
u_long ntohl (u_long netlong)
u_short ntohs (u_short netshort)
• Las primera traduce un número de 32 bits representado
en el formato del computador al formato de red (TCP/IP).
Sockets
• Abstracción que representa un extremo en la
comunicación bidireccional entre dos procesos.
• Ofrecen una interfaz para acceder a los servicios de red
en el nivel de transporte de los protocolos TCP/IP.
• Prácticamente disponibles en todos los sistemas tipo
Unix/Linux, Windows…
Sockets (2)
• Comunicación con sockets
• Dominios de comunicación
• PF_UNIX (dentro de la misma computadora)
• PF_INET (procesos en computadoras diferentes)
• Tipos de sockets
• Orientados a conexión (SOCK_STREAM)
• No orientados a conexión (SOCK_DGRAM)
• Direcciones de sockets
• AF_UNIX para sockets del dominio UNIX.
• AF_INET para sockets del domino Internet.
Sockets (3)
• Escenario típico con sockets de tipo stream
Sockets (3)
• Ejemplo sencillo
• Aplicación servidor para sumar dos enteros
32
SERVIDOR:
Pide un socket al SO (punto o puerto de conexión a un proceso)
• Llamada al sistema:
s=socket(dominio, tipo, protocolo)
– Dominio: AF_UNIX rutas de ficheros (especifica conexión local en la misma
máquina) y AF_INET para direcciones de internet (en máquina remota)
– Tipo: para mandar datagramas SOCK_DGRAM o orientado a conexión
SOCK_STREAM
– Protocolo: 0 indica IP, SOCK_STREAM para TCP y SOCK_DGRAM UDP

• El SO devuelve ese sockets que es un descriptor de archivo asociado al


proceso que lo creo y con las propiedades que se pasan como argumento
Informar al SO que estoy dispuesto a recibir conexiones
• Llamada al sistema:
bind(descriptorsocket, dirección, longitud)
• Especifica al SO la dirección asignada al socket por donde puedo recibir
mensajes (puerto 7000)
33

SERVIDOR:
Tabla para guardar peticiones pendientes:
• Llamada al sistema:
estado=listen(descriptorsocket, longitud)
• Reserva de zona de memoria para guardar peticiones que llegan para
atenderlas posteriormente
Resumen: El proceso servidor
– Quiero recibir
– en tal puerto
– sino puedo atender guarda petición en memoria
• Además debe esperar que alguien se conecte para ello usa la llamada:
Nuevo socket=accept(antiguosocket, direccion, longitud)
– El descriptor nuevo socket (que es el usado para comunicar) es idéntico al
antiguosocket. Recibe la dirección del socket activo (el del cliente) en
direccion
– Si no conexiones pendientes antiguosocket bloquea al proceso hasta que
lleguen .Al acabar se debe cerrar nuevo socket close() para dar fin a
comunicación
CLIENTE: 34

Pide un socket al SO (punto o puerto de conexión a un proceso)


• Llamada al sistema:
r=socket(dominio, tipo, protocolo)
• Llamada al sistema:
connect(descritorsocketactivo, direccion, longitud)
– Establece la conexión entre socket activo y el pasivo para mandarle información
– Direccion y longitud se refieren al socketpasivo o de destino

• En el boletín de prácticas se nos dan dos programas: client.c server.c


– Estudiar el programa intentando identificar su funcionamiento
– Compilar primero servidor, ejecutarlo en una terminal de vuestro ordenador
– Compilar el cliente y ejecutarlo en otra terminal de vuestro ordenador
pasandole los parámetros necesarios:
• Nombre máquina a la que quiero conectar
• Puerto donde escucha el servidor
• Palabra o frase (si es frase entre comillas dobles) a enviar
– ¿Por qué aparecen unos caracteres extraños?
Creación de un socket
• int socket(int dominio, int tipo, int protocolo)
• Crea un socket devolviendo un descriptor de fichero
• dominio: PF_XXX
• tipo: SOCK_XXX
• protocolo: dependiente del dominio y tipo
• 0 elige el más adeucado
• Especificados en /etc/protocols

• El socket creado no tiene dirección asignada


Asignación de direcciones
• int bind(int sd, struct sockaddr *dir, int long)
• sd: descriptor devuelto por socket
• dir: dirección a asignar
• long: longitud de la dirección
• Si no se asigna dirección (típico en clientes)
• Se le asigna automáticamente (puerto efímero) en la su primera
utilización (connect o sendto)
• Direcciones en dominio PF_INET
• Puertos en rango 0..65535. Reservados: 0..1023. Si 0, el sistema
elige uno
• Host: una dirección local IP
• INNADDR_ANY: elige cualquiera de la máquina
• El espacio de puertos para streams y datagramas es
indendiente
Solicitud de conexión
• Realizada en el cliente
• int connect(int sd, struct sockaddr *dir, int long)
• sd: descriptor devuelto por socket
• dir: dirección del socket remoto
• long: longitud de la dirección
• Si el socket no tiene dirección asignada, se le asigna una
automáticamente
• Normalmente se usa con streams
Preparar para aceptar conexiones
• Realizada en el servidor stream después de socket y bind
• int listen(int sd, int baklog)
• sd: descriptor devuelto por socket
• backlog:
• Número máximo de peticiones pendientes de aceptar que se encolarán
(algunos manuales recomiendan 5)
• Hace que el socket quede preparado para aceptar
conexiones.
Aceptar una conexión
• Realizada en el servidor stream después de socket, bind
y listen
• Cuando se produce la conexión, el servidor obtiene:
• La dirección del socket del cliente
• Un nuevo descriptor que queda conectado al socket del cliente
• Después de la conexión quedan activos dos sockets en el
servidor:
• El original para aceptar nuevas conexiones
• El nuevo para enviar/recibir datos por la conexión
Aceptar una conexión
• int accept(int sd, struct sockaddr *dir, int *long)
• sd: descriptor devuelto por socket
• dir: dirección del socket del cliente devuelta
• long: parámetor valor-resultado
• Antes de la llamada: tamaño de dir
• Después de la llamada: tamaño de la dirección del cliente que se
devuelve.
Obtener la dirección de un socket
• Obtener la dirección a partir del descriptor
• int getsockname(int sd, struct sockaddr *dir, int *long)
• sd: descriptor devuelto por socket
• dir: dirección del socket devuelta
• long: parámetro valor-resultado (igual que en accept)

• Obtener la dirección del socket en el toro extremo de la


conexión:
• int gerpeername(int sd, struct sockaddr *dir, int *long)
• sd: descriptor devuelto por socket
• dir: dirección del socket remoto
• long: parámetro valor-resultado
Transferencia de datos con streams
• Una vez realizada la conexión, ambos extremos puede
transferir datos.
• Envío:
• int write(int sd, char *mem, int long);
• Devuelve el nº de bytes enviados
• También puede utilizarse el servicio send.
• Recepción:
• int read(int sd, char *mem, int long);
• Devuelve el nº de bytes recibidos
• También puede utilizarse el servicio recv
• Es importante comprobar siempre el valor que devuelven
estas llamadas: pueden no transferirse todos los datos.
Transferencia de datos con streams II
• Función que envía un bloque de datos con reintentos:

int enviar(int socket, char *mensaje, int longitud)


{
int r;
int l = longitud;
do {
r = write(socket, mensaje, l);
l = l – r;
mensaje = mensaje + r;
} while ((l>0) && (r>=0));
if (r < 0)
return (-1); /* fallo */
else
return(0);
}
Transferencia de datos con datagramas
• No hay conexión real
• Para usar un socket para transferir basta con:
• Crearlo: socket
• Asignarle una dirección: bind (si no, lo hará el sistema)
• Envío:
• int sendto(int sd, char *men, int long, int flags,
struct sockaddr *dir, int long)
• Devuelve el nº de bytes enviados
• dir: dirección del socket remoto y long la longitud
• Rccepción:
• int recvfrom(int sd, char *men, int long, int flags,
struct sockaddr *dir, int long)
• Devuelve el nº de bytes enviados
• dir: dirección del socket remoto y long la longitud
Cerrar un socket
• Se usa close para cerrar ambos tipos de sockets
• Si el socket es de tipo stream, close cierra la conexión en
ambos sentidos
• Se puede cerrar un único extremo:
• int shutdown(int st, int modo)
• sd: descriptor devuelto por socket
• modo: SHUT_RD, SHUT_RW o SHUT_RDWR
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
• int getsockopt(int sd, int nivel, int opc, char *val, int *long)
• Modificar las opciones asociadas a un socket
• int setsockopt(int sd, int nivel, int opc, char *val, int long)
• Ejemplos (nivel SOL_SOCKET):
• SO_REUSEADDR: permite reutilizar direcciones
Escenario típico con sockets
Proceso servidor
streams
socket()

Proceso cliente
bind()

socket() listen()

Abrir conexión
accept() Crear
connect()
thread
accept()

Petición
write() read()

Respuesta
read() write()

close() close()
Ejemplo (TCP)
Máquina A Máquina B
sumar(5,2)
cliente servidor
5+2

NÚCLEO Restulado = 7 NÚCLEO

RED
Servidor (TCP)
void main(int argc, char *argv[])
{
struct sockaddr_in server_addr, client_addr;
int sd, sc;
int size, val;
int size;
int num[2], res;

sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


val = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(int));

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


server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = 4200;

bind(sd, &server_addr, sizeof(server_addr));


Servidor (TCP)
listen(sd, 5);
size = sizeof(client_addr);
while (1)
{
printf("esperando conexion\n");
sc = accept(sd, (struct sockaddr *)&client_addr,&size);

read(sc, (char *) num, 2 *sizeof(int)); // recibe la petición

res = num[0] + num[1];

write(sc, &res, sizeof(int)); // se envía el resultado

close(sc);
}

close (sd);
exit(0);
}
Cliente (TCP)
void main(void)
{
int sd;
struct sockaddr_in server_addr;
struct hostent *hp;
int num[2], res;

sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

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


hp = gethostbyname ("arlo.datsi.fi.upm.es");

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


server_addr.sin_family = AF_INET;
server_addr.sin_port = 4200;
Cliente (TCP)
// se establece la conexión
connect(sd, (struct sockaddr *) &server_addr,

sizeof(server_addr));

num[0]=5;
num[1]=2;

write(sd, (char *) num, 2 *sizeof(int)); // envía la petición

read(sd, &res, sizeof(int)); // recibe la respuesta

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


close (sd);
exit(0);
}
Servidor (datagramas)
void main(void)
{
int num[2];
int s, res, clilen;
struct sockaddr_in server_addr, client_addr;

s = socket(AF_INET, SOCK_DGRAM, 0);

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


server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = 7200;

bind(s, (struct sockaddr *)&server_addr, sizeof(server_addr));


Servidor (datagramas)
clilen = sizeof(client_addr);

while (1)
{
recvfrom(s, (char *) num, 2* sizeof(int), 0,
(struct sockaddr *)&client_addr, &clilen);

res = num[0] + num[1];

sendto(s, (char *)&res, sizeof(int), 0,


(struct sockaddr *)&client_addr, clilen);
}

}
Cliente (datagramas)
void main(int argc, char *argv[]){
struct sockaddr_in server_addr, client_addr;
struct hostent *hp;
int s, num[2], res;

if (argc != 2){
printf("Uso: client <direccion_servidor> \n");
exit(0);
}

s = socket(AF_INET, SOCK_DGRAM, 0);


hp = gethostbyname (argv[1]);

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


server_addr.sin_family = AF_INET;
memcpy (&(server_addr.sin_addr), hp->h_addr, hp->h_length);
server_addr.sin_port = 7200;
Cliente (datagramas)
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));

num[0] = 2; num[1] = 5;

sendto(s, (char *)num, 2 * sizeof(int), 0,


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

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

printf("2 + 5 = %d\n", res);


close(s);
}

También podría gustarte