Programación con sockets Internetworking with TCP/IP Client/Server Programming and Applications: Linux/POSIX Sockets Version Douglas E. Comer David L. Stevens Capítulos: 2, 5, 7 Grupo de Sistemas y Comunicaciones (GSyC) Modelo cliente/servidor Cliente: programa que inicia una comunicación. Servidor: programa que espera la llegada de peticiones de comunicación de los clientes. Normalmente varios clientes conectados a un mismo servidor cliente 1. Petición 2. Respuesta servidor cliente cliente Existen programas que funcionan a la vez como clientes y como servidores Redes de Área Local 2007 2 1
TCP vs. UDP UDP: No orientado a conexión No fiable: duplicados, pérdidas, desorden Suele funcionar razonablemente bien en redes locales. Ejemplos: DNS, NFS, SNMP TCP: Orientado a conexión Fiable: sin duplicados, sin pérdidas, en orden Control de flujo y control de congestión Más pesado que UDP. Ejemplos: FTP, HTTP, SMTP Redes de Área Local 2007 3 Servidores con/sin estado Estado: información que mantiene el servidor sobre el estado de las comunicaciones con los clientes. Servidores con estado: mantienen información sobre las comunicaciones con los clientes. La información de estado permite que el servidor tenga información sobre las operaciones previas que ha realizado el cliente, permitiendo que el servidor responda incrementalmente. Servidores sin estado: no guardan este tipo de información. La información de estado puede resultar errónea si se pierden, duplican o se entregan los paquetes desordenados, pudiendo provocar errores en las respuestas del servidor. Redes de Área Local 2007 4 2
Interfaz de comunicaciones Apl1 Apl2 AplN Aplicaciones en el espacio de direcciones de usuario Llamadas al sistema realizadas por las aplicaciones Interfaz de llamadas al sistema Núcleo del sistema operativo que contiene la pila de protocolos TCP/IP Software de la pila de protocolos en el espacio de direcciones del núcleo Redes de Área Local 2007 5 Interfaz de sockets En 1980 ARPA (Advanced Research Projects Agency) financió un grupo de investigación en la Universidad de California en Berkeley para implementar el software de TCP/IP en UNIX: Crearon una interfaz que las aplicaciones usan para comunicarse. Utilizaron las llamadas al sistema de UNIX y añadieron funciones nuevas. Se denominó interfaz de sockets y el sistema se llamó Berkeley UNIX o BSD UNIX La interfaz de sockets ha sido aceptada por fabricantes de estaciones de trabajo, convirtiéndose en un standard de facto. Redes de Área Local 2007 6 3
Qué es un socket Un socket es un descriptor que permite leer/escribir datos destinados a otro proceso, en la misma máquina o en máquinas diferentes utilizando para ello la red. De forma equivalente a un descriptor de fichero que permite leer/escribir datos en un fichero. Lo utilizan las aplicaciones como extremo de un canal de comunicaciones. Para poder intercambiar datos las aplicaciones tienen que crear un socket que les permite enviar y recibir datos. El destino de la comunicación se identifica por la dirección IP + Ap1 Ap2 puerto. API sockets TCP/UDP IP Ethernet API sockets TCP/UDP IP Ethernet Redes de Área Local 2007 7 Estructuras de dirección de socket Dirección genérica de socket: Argumento de las funciones del API de sockets. struct sockaddr { unsigned short sa_family; /* familia */ char sa_data[14]; /* datos de la dir. */ }; Dirección de socket de Internet (IPv4): dirección IP + puerto Es necesario hacer cast de (sockaddr_in *) a (sockaddr *) para las funciones del API de sockets. struct sockaddr_in { unsigned short sin_family; /* familia (= AF_INET) */ unsigned short sin_port; /* puerto en orden de red */ struct in_addr sin_addr; /* dir. IP, en formato de red */ unsigned char sin_zero[8]; /* no utilizado */ }; struct in_addr { unsigned long s_addr; /* 32 bits */ }; Redes de Área Local 2007 8 4
Orden de máquina y Orden de red Diferentes formas en las que un sistema puede almacenar datos en la memoria. De orden de máquina a orden de red: uint32_t htonl(uint32_t hostlong); uint16_t htonl(uint16_t hostshort); De orden de red a orden de máquina: uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort); Orden de red: posición más alta el byte menos significativo Orden de máquina: posición más alta el byte más significativo N N+1 MSB LSB N N+1 LSB MSB Redes de Área Local 2007 9 Cliente y Servidor UDP Client socket Server socket bind bind recvfrom sendto sendto recvfrom close Redes de Área Local 2007 10 5
socket() Crea un descriptor de socket: int socket(int dominio, int tipo, int protocolo); Dominio : AF_INET para Internet Tipo: SOCK_STREAM para TCP SOCK_DGRAM para UDP. Protocolo: 0 El valor de retorno es el descriptor de socket, sd. Es necesario comprobar siempre si ha ocurrido un error en la llamada socket() Si sd<0 se ha producido un error. Redes de Área Local 2007 11 bind() Asocia una dirección IP y un puerto (dirección de socket) de la máquina LOCAL a un socket. int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen); sockfd: Descriptor de socket my_addr: Dirección del socket de la máquina LOCAL.. Será necesario realizar cast de (sockaddr_in *) a (sockaddr *). addrlen: tamaño de la dirección de socket El valor de retorno indica si la conexión se ha establecido correctamente. Es necesario comprobar este valor: 0: la conexión se ha establecido -1: la conexión ha fallado El servidor obligatoriamente tiene que llamar a bind() utilizando la dirección IP de su máquina y un número de puerto que deberá conocer el cliente para poder conectarse (rellenar serv_addr de connect()). El servidor puede utilizar: my_addr.sin_addr=inaddr_any para recibir información en cualquiera de las IPs que tenga configurada la máquina. El cliente no tiene por qué llamar a bind(). Puede hacerlo con my_addr.sin_port=0: el sistema le asigna un puerto libre. Redes de Área Local 2007 12 6
Escribe los bytes de un descriptor. sendto() int sendto(int sockfd, void *buf, size_t nbytes, int flags, const struct sockaddr *to_addr, socklen_t to_addr_len); sockfd: Descriptor de socket buf: Contiene los datos a enviar nbytes: número de bytes que se solicita enviar. flags: opciones de configuración. Valor 0 si no hay opciones. to_addr: Contiene la dirección del socket receptor (dirección IP y puerto). to_addr_len: Contiene el tamaño de la estructura to_addr. Redes de Área Local 2007 13 recvfrom() Lee los bytes de un descriptor. Por defecto es una llamada bloqueante, si no hay bytes se quedará esperando. int recvfrom(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *from_addr, socklen_t *from_addr_len); sockfd: Descriptor de socket buf: Buffer donde se almacenarán los bytes leidos. Será necesario reservar memoria. nbytes: número de bytes que se solicita leer. flags: opciones de configuración. Valor 0 si no hay opciones. from_addr: Cuando la llamada retorne tendrá almacenada la dirección del socket emisor (dirección IP y puerto). Será necesario reservar memoria. from_addr_len: parámetro de entrada/salida, al realizar la llamada contiene el tamaño de la estructura from_addr, al retornar contiene el tamaño de la estructura que ha sido devuelta. Redes de Área Local 2007 14 7
socklen_t y sockaddr: parámetros de entrada y/o salida sendto, bind : socklen_t: parámetro de entrada, paso por valor sockaddr_in: parámetro de entrada, paso por referencia recvfrom socklen_t: parámetro de entrada y salida, paso por referencia sockaddr_in: parámetro de salida, paso por referencia sendto, bind Aplicación recvfrom Aplicación socklen_t sockaddr_in socklen_t sockaddr_in valor dirección dirección dirección Kernel Kernel Redes de Área Local 2007 15 Cierra el descriptor de socket. close() int close(int sockfd); sockfd: Descriptor de socket El valor de retorno indica si ha habido algún error. Es necesario comprobar este valor: 0: éxito -1: error Redes de Área Local 2007 16 8
Dirección IP: Formato ASCII y formato de red De formato de red a formato ASCII: const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); af: familia, INET_AF src: una estructura de tipo in_addr en orden de red. dst: cadena de caracteres que contendrá el resultado de la conversión size: tamaño de la cadena de caracteres Si el valor de retorno es NULL ha ocurrido un error Redes de Área Local 2007 17 Dirección IP: Formato ASCII y formato de red De formato ASCII a formato de red: int inet_pton(int af, const char *src, void *dst); af: familia, INET_AF src: cadena de caracteres que contendrá la dirección IP. dst: una estructura de tipo in_addr en orden de red, resultado de la conversión Si el valor de retorno es 0 ha ocurrido algún error. Redes de Área Local 2007 18 9
Servidor UDP (I) #define PUERTO_UDP 11000 #define UNIXEPOCH 2208988800UL /* UNIX epoch, in UCT secs */ int main() { int s, leidos, escritos; struct sockaddr_in srv_addr, client_addr; socklen_t len_srv_addr, client_addr_len; char buff[1]; time_t now; if ((s= socket(af_inet, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "error en socket %s\n", strerror(errno)); exit(1); } srv_addr.sin_family=af_inet; srv_addr.sin_port = htons(puerto_udp); srv_addr.sin_addr.s_addr = INADDR_ANY; len_srv_addr = sizeof(srv_addr); if (bind(s, (struct sockaddr *) &srv_addr, len_srv_addr)<0) { fprintf(stderr, "error en bind %s\n", strerror(errno)); exit(1); } Redes de Área Local 2007 19 Servidor UDP (II) client_addr_len = sizeof(client_addr_len); while (1) { memset(&client_addr, 0, sizeof(client_addr)); leidos = recvfrom(s, buff, sizeof(buff), 0, (struct sockaddr * ) &client_addr, &client_addr_len); if (leidos < 0) { fprintf(stderr, "error en recvfrom %s\n", strerror(errno)); exit(1); } time(&now); now = htonl((unsigned long)(now + UNIXEPOCH)); escritos = sendto(s, (char *)&now, sizeof(now), 0, (struct sockaddr *)&client_addr, sizeof(client_addr)); } /* while */ } /* main */ Redes de Área Local 2007 20 10
Cliente UDP (I) #define PUERTO_UDP 11000 #define UNIXEPOCH 2208988800UL /* UNIX epoch, in UCT secs */ #define MSG "Dime la hora" int main() { int s, escritos, leidos; struct sockaddr_in srv_addr, from_addr; socklen_t srv_addr_len, from_addr_len; time_t now; if ((s= socket(af_inet, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "error en socket %s\n", strerror(errno)); exit(1); } srv_addr.sin_family=af_inet; srv_addr.sin_port = htons(puerto_udp); srv_addr.sin_addr.s_addr = inet_addr("212.128.4.197"); srv_addr_len = sizeof(srv_addr); escritos = sendto(s, MSG, strlen(msg), 0, (struct sockaddr * ) &srv_addr, srv_addr_len); if (escritos < 0) { fprintf(stderr, "error en sendto %s\n", strerror(errno)); exit(1); } Redes de Área Local 2007 21 Cliente UDP (II) memset(&from_addr, 0, sizeof(from_addr)); from_addr_len = sizeof(from_addr); leidos = recvfrom(s, (char *)&now, sizeof(now), 0, (struct sockaddr *)&from_addr, &from_addr_len); now = ntohl((unsigned long)now); now -= UNIXEPOCH; } printf("hora: %s\n", ctime(&now)); Redes de Área Local 2007 22 11
Netstat Netstat: Muestra las conexiones de red, tablas de encaminamiento, estadísticas de interfaces de una máquina. eva@alpha27:$ netstat -anu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State udp 0 0 0.0.0.0:1024 0.0.0.0:* udp 0 0 0.0.0.0:1025 0.0.0.0:* udp 0 0 0.0.0.0:849 0.0.0.0:* udp 0 0 0.0.0.0:990 0.0.0.0:* udp 0 0 0.0.0.0:111 0.0.0.0:* udp 0 0 0.0.0.0:11000 0.0.0.0:* Redes de Área Local 2007 23 12