180 likes | 290 Views
Capítulo 22: Interfaz de Socket. ICD 327: Redes de Computadores Agustín J. González. Introducción. ¿Cómo las aplicaciones se comunican con la familia de protocolos de software y así logran comunicación con aplicaciones remotas?
E N D
Capítulo 22: Interfaz de Socket ICD 327: Redes de Computadores Agustín J. González
Introducción • ¿Cómo las aplicaciones se comunican con la familia de protocolos de software y así logran comunicación con aplicaciones remotas? • La interfaz de programas de aplicación usada para inter-actuar con los protocolos de red se conocen como las APIs para la programación de red. La más conocida y difundida es la API de Socket (Socket Application Program Interface). • A través de la interfaz de socket, las aplicaciones especifican los detalles de la comunicación como: qué protocolo se usará, es una aplicación cliente o servidor, y máquina remota. • En el sistema operativo UNIX esta API es parte del SO. Otros sistemas operativos han creado una biblioteca (library) que implementa todos los llamados de la API de socket y así el código UNIX puede ser fácilmente compilado y ejecutado.
Modelo de comunicación de Socket y I/O en UNIX • El modelo empleado en socket para la comunicación entre aplicaciones remotas es similar al usado en la comunicación con cualquier dispositivo de I/O en UNIX. Enviar datos a una aplicación remota usa el mismo tipo de llamados que enviar datos a un archivo. • El modelo se conoce con el nombre de paradigma open-read-write-close (abrir-leer-escribir-cerrar). El nombre refleja la secuencia típica para una la transferencia de información. • En UNIX cuando se abre un archivo se retorna un descriptor de archivo. Cuando se abre un socket también se retorna un descriptor. • Un descriptor es un entero. El SO usa este entero para entrar en una tabla que contiene todos los atributos del dispositivo al cual se refiere el descriptor.
Parámetros usados en la API de Sockets • Las mayores diferencias entre la comunicación vía sockets y manejo de archivos es la la forma de abrir el canal. • Para crear un sockets la aplicación debe especificar : • Protocolo de transporte (su familia y protocolo dentro de la familia) • Dirección de la máquina remota o dirección local donde se esperará por requerimientos remotos, • Es una cliente o un servidor, • El puerto asociado a la aplicación
Creación y cierre de un socket • Creación:int descriptor;descriptor = socket(protocolFamily, type, protocol);donde: • Familia de protocolo: PF_INET, PF_APPLETALK • Tipo: SOCK_STREAM para servicios de conexión y SOCK_DGRAM para servicios sin conexión. • Protocolo: El protocolo particular a ser usado de la familia de protocolo. En TCP/IP, éste puede ser tcp o udp. • Cierre del un socket:close(descriptor);Se termina la conexión antes de cerrar el socket.
Procedimientos en servidor • Procedimiento bindEste procedimiento asocia o liga un socket con una dirección IP y un puerto local.bind(descriptor, local_addr, addr_len); • Descriptor: es el descriptor del socket. • Dirección local: es una estructuraconteniendo IP local y puerto local. • Largo de dirección: entero que especifica el largo de la estructura local_addr. • Como el llamado debe servir para varios protocolos, la estructura en genérica:
Procedimientos en servidor (cont..) • struct sockaddr { u_char sa_len; // largo total u_char sa_family; // familia de la dirección char sa_data[14]; //la dirección } • En el caso particular de TCP/IP el campo dirección está especificado como sigue: • struct sockaddr_in { u_char sin_len; // largo total u_char sin_family; // familia de la dirección u_short sin_port; // número de puerto struct in_addr sin_addr; // dirección IP char sin_zero[8]; // no usados }
Procedimientos en servidor (cont..) • Procedimiento listenlisten( descriptor, queue_size);Instruye al OS que el socket es pasivo y desea tener a lo más queu_size requerimientos de conexión pendientes. • El sistema rechaza requerimientos cuando la cola se llena. • Procedimiento acceptnewDescriptor = accept(descriptor, c_address, c_address_len); • Esta llamada es usada por servidores que usan servicios de conexión. • c_address es del tipo struct sockaddr ya visto. • Luego de aceptar una conexión, el servidor atiende al ciente a través del descriptor especialmente creado para él. Mientras tanto el servidor puede aceptar nuevas conexiones en el descriptor original.
Procedimientos en cliente • Procedimiento connectconnect(descriptor, srv_address, srv_address_len);srv_address: es una estructura del tipo struct sockaddr que contiene la dirección del servidor.srv_address_len es el largo de la estructura. • Cuando el servicio es de conexión, connect establece la conexión con el servidor, el cual debe aceptar con accept(). • Cuando el servicio es sin conexión, connect registra los datos del servidor al cual se le enviarán los datagramas posteriormente. Así no es necesario especificar el destinatario en cada datagrama.
Procedimientos de envío y recibo • Procedimiento sendint send (descriptor, data, length, flags);data es la dirección de memoria donde se encuentran los datos a enviar.Length: es el largo en bytes de la zona de datos. Ésta función retorna el número de bytes efectivamente enviados Flags: especifican opciones. Las aplicaciones normales no las usan. Son usadas por programas de monitoreo y debug. • Procedimiento sendtoint sendto (descriptor, data, length, flags, dest_address, address_len);
Procedimientos de envío y recibo • Procedimiento recvint recv (descriptor, buffer, length, flags);buffer es la dirección de memoria donde se deben depositar los datos a recibir.length: es el largo en bytes de los datos que se desean leer. Recv retorna el número de bytes efectivamente leídos.Flags: especifican opciones. Las aplicaciones normales no las usan. Son usadas por programas de monitoreo y debug. • Procedimiento recvfromint recvfrom (descriptor, buffer, length, flags, source_address, address_len);
Procedimientos de envío y recibo (cont..) • Procedimientos read y writeint read (descriptor, buffer, length);int write( descriptor, data, length); • Estos dos llamados son similares a send y recv, y se pueden usar indistintamente en servicios de conexión. • Estos procedimientos no funcionan en servicios sin conexión. • La gran ventaja de estos procedimientos es su generalidad. Las aplicaciones pueden transferir datos sin saber si se trata de un descriptor de archivo o un socket.
Ejemplo: Servidor UDP #define PORTNUMBER 12345 int main(void) { char buf[1024]; int s, n, len; struct sockaddr_in name; s = socket(PF_INET, SOCK_DGRAM, 0); /* Create the socket. */ name.sin_family = AF_INET; /* Create the address of the server. */ name.sin_port = htons(PORTNUMBER); name.sin_addr.s_addr = htonl(INADDR_ANY); /* Use the wildcard address.*/ len = sizeof(struct sockaddr_in); bind(s, (struct sockaddr *) &name, len); /* Bind the socket to the address. */ while ((n = recv(s, buf, sizeof(buf), 0)) > 0) /* Read from the socket until end-of-file */ write(stdout, buf, n); /* and print what we get on the standard output. */ close(s); }
#define PORTNUMBER 12345 int main(void) { int n, s, len; char buf[1024]; char hostname[64]; struct hostent *hp; struct sockaddr_in name; gethostname(hostname, sizeof(hostname)); /* Get our local host name. */ hp = gethostbyname(hostname); /* Look up our host's network address. */ s = socket(PF_INET, SOCK_DGRAM, 0); /* Create a socket in the INET domain. */ name.sin_family = AF_INET; /* Create the address of the server. */ name.sin_port = htons(PORTNUMBER); memcpy(&name.sin_addr, hp->h_addr_list[0], hp->h_length); /* set IP address */ len = sizeof(struct sockaddr_in); while ((n = read(0, buf, sizeof(buf))) > 0) /* Read from standard input, and */ sendto(s, buf, n, 0, (struct sockaddr*) &name, len); /* copy the data to the socket. */ sendto(s, buf, 0, 0, (struct sockaddr*) &name, len); close(s); } Ejemplo: Cliente UDP
Otras funciones relacionadas • Procedimientos getpeername: permite obtener la dirección y puerta remota a partir de un socket ya “conectado”. • Procedimiento gethostname: permite obtener el nombre de la máquina local. • Procedimientos getsockopt y setsockopt: permiten obtener y modificar atributos de un socket; por ejemplo, el tramaño del buffer usado por TCP para recibir paquetes. • Procedimientos gethostbyname y gethostbyaddr: permiten obtener la información sobre una máquina a partir de su nombre o dirección IP.
Cliente/Servidor TCP: Secuencia de Eventos Send/recv Send/recv
Ejemplo: Servidor TCP #define PORTNUMBER 12345 int main(void) { char buf[1024]; int s, n ,ns, len; struct sockaddr_in name; s = socket(PF_INET, SOCK_STREAM, 0); /* Create the socket. */ name.sin_family = AF_INET; /* Create the address of the server. */ name.sin_port = htons(PORTNUMBER); name.sin_addr.s_addr = htonl(INADDR_ANY); /* Use the wildcard address.*/ len = sizeof(struct sockaddr_in); bind(s, (struct sockaddr *) &name, len); /* Bind the socket to the address. */ listen(s, 5); /* Listen for connections. */ ns = accept s,(struct scoket *) &name, &len); /* Accept a connection */ while ((n = recv(ns, buf, sizeof(buf), 0)) > 0) /* Read from the socket until end-of-file */ write(stdout, buf, n); /* and print what we get on the standard output. */ close(ns); close(s); }
#define PORTNUMBER 12345 int main(void) { int n, s, len; char buf[1024]; char hostname[64]; struct hostent *hp; struct sockaddr_in name; gethostname(hostname, sizeof(hostname)); /* Get our local host name. */ hp = gethostbyname(hostname); /* Look up our host's network address. */ s = socket(PF_INET, SOCK_STREAM, 0); /* Create a socket in the INET domain. */ name.sin_family = AF_INET; /* Create the address of the server. */ name.sin_port = htons(PORTNUMBER); memcpy(&name.sin_addr, hp->h_addr_list[0], hp->h_length); /* set IP address */ len = sizeof(struct sockaddr_in); connect(s, (struct sockaddr *) &name, len); /* Connect to the server */ while ((n = read(stdin, buf, sizeof(buf))) > 0) /* Read from standard input, and */ send(s, buf, n, 0); /* copy the data to the socket. */ /* OUT sendto(s, buf, 0, 0, (struct sockaddr*) &name, len); */ close(s); } Ejemplo: Cliente TCP