¡Descarga Explicacion de SOCKETs y más Diapositivas en PDF de Sistemas Operativos solo en Docsity!
PROGRAMACIÓN CON SOCKETS
Pág. 1
PROGRAMACIÓN CON SOCKETS
ÍNDICE
1. Introducción.
2. Estructuras y manipulación de datos.
3. API de sockets (llamadas al sistema).
4. Esquemas para servidor/cliente TCP, servidor/cliente UDP y
para servidor TCP concurrente.
Pág. 2
PROGRAMACIÓN CON SOCKETS
BIBLIOGRAFÍA
• Básica:
– W. R. Stevens: “Unix Network Programming”. Volume 1:
Networking APIs-Sockets and XTI. 1998 (L/S 004.451.9 UNIX
STE ).
– Capítulo 22 de D. E. Comer: “Internetworking with TCP/IP vol I”,
2 Ed. Prentice Hall, 2000.
– Beej's Guide to Network Programming
(http://www.it.uc3m.es/celeste/docencia/inf/rroo/practica/sockets
.html).
• Complementaria:
– http://www.gnu.org/software/libc/manual/html_node/Sockets.ht
ml
Pág. 3
PROGRAMACIÓN CON SOCKETS - INTRODUCCIÓN
Introducción
• TCP/IP no define el interfaz con los programas de aplicación (API).
- Depende mucho del sistema operativo.
• El interfaz socket , de BSD UNIX, se ha convertido en el estándar
“de facto”.
- A principios de los 80s, ARPA funda un grupo en la Universidad de
California en Berkeley para integrar TCP/IP en el sistema operativo
UNIX
- El sistema que desarrollaron se conoce como Berkeley UNIX o BSD
UNIX.
- Como parte del proyecto, se define un API para que las aplicaciones
puedan usar TCP/IP para comunicarse: interfaz socket.
- Comunicación entre procesos (IPC).
- Aparece a partir de la versión 4.1 de BSD
- Se basa en las llamadas de E/S de UNIX.
• En el interfaz socket se basan los APIs de otros sistemas (como el
WinSock ).
Pág. 6
PROGRAMACIÓN CON SOCKETS - INTRODUCCIÓN
Comunicación entre procesos (IPC)
• Sigue una estructura parecida a la Entrada/Salida:
Crear socket
Conectar
Enviar
Recibir
Cerrar socket
Pág. 7
PROGRAMACIÓN CON SOCKETS - ESTRUCTURAS Y MANIPULACIÓN DE DATOS
Manipulación de datos: ordenación de octetos
- Dos posibles ordenaciones de los octetos:
- Primer octeto es el más significativo.
- Primer octeto es el menos significativo.
- La “ordenación de octetos de la red” sigue la primera ordenación (“ Big-
Endian ”), pero los ordenadores pueden almacenar sus datos de cualquier
de la dos formas, por lo tanto en algunos casos es preciso realizar
conversiones.
- Todo lo que se envíe por la red debe seguir la “ordenación de la red”.
- Métodos de conversión (#include <netinet/in.h>):
- htons(): “ Host to Network Short ” (short de máquina a short de red).
- htonl(): “ Host to Network Long ” (long de máquina a long de red).
- ntohs(): “ Network to Host Short ” (short de la red a short de la máquina).
- ntohl() : “ Network to Host Long ” (long de red a long de máquina).
Pág. 8
PROGRAMACIÓN CON SOCKETS - ESTRUCTURAS Y MANIPULACIÓN DE DATOS
Estructura sockaddr
• Mantiene información sobre direcciones de sockets.
• Campos:
- sa_family: familia de direcciones (admite varios valores, en nuestro
caso AF_INET).
- sa_data: dirección y número de puerto para el socket.
struct sockaddr { unsigned short sa_family; char sa_data[14]; };
Pág. 9
PROGRAMACIÓN CON SOCKETS - ESTRUCTURA Y MANIPULACIÓN DE DATOS
Estructura sockaddr_in
- Mantiene información sobre direcciones de sockets (más fácil manejo que
la estructura anterior).
- Campos:
- sin_family: admite varios valores, en nuestro caso AF_INET.
- sin_port: número de puerto para el socket.
- sin_addr: dirección IP para el socket.
- sin_zero: relleno para mantener el mismo tamaño que sockaddr (debe rellenarse a cero).
- Notas:
- sin_port y sin_addr deben seguir la “ordenación de octetos de la red”.
- Es posible hacer “casting” entre estructuras sockaddr_in y sockaddr.
struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; };
Pág. 12
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: bind()
- sockfd: descriptor del socket.
- my_addr: es un puntero a una estructura sockaddr.
- addrlen: longitud de la estructura anterior.
- Resultado:
- “-1” si se ha producido un error.
#include <sys/types.h> #include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
Pág. 13
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: bind()
my_addr.sin_port = 0; /* selecciona, aleatoriamente, un puerto sin usar / my_addr.sin_addr.s_addr = htnol(INADDR_ANY); / usa mi dirección IP */
• Consideraciones sobre la elección de puerto:
- Todos los puertos por debajo del 1024 están RESERVADOS, a menos
que seas súper-usuario.
- Puedes usar cualquier número de puerto por encima de ese, hasta el
65535, siempre y cuando no lo esté usando ya otro programa.
• La llamada a bind se emplea en programas servidores, en
programas clientes no es necesario realizar esta llamada (se llama
a connect).
Pág. 14
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: ejemplo de bind()
#include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h>
#define MYPORT 3490
void main() { int sockfd; struct sockaddr_in my_addr;
/* Chequear si devuelve -1! / sockfd = socket(AF_INET, SOCK_STREAM, 0); my_addr.sin_family = AF_INET; / Ordenación de la máquina / my_addr.sin_port = htons(MYPORT); / Ordenación de la red / my_addr.sin_addr.s_addr = inet_addr("132.241.5.10"); / Pone a cero el resto de la estructura / memset(&(my_addr.sin_zero), '\0', 8); / Chequear si devuelve -1! */ bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); ...
Pág. 15
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: connect()
- sockfd: descriptor del socket.
- serv_addr: es un puntero a una estructura sockaddr que contiene el
puerto y la dirección IP del destino.
- addrlen: longitud de la estructura anterior.
- Resultado:
- “-1” si se ha producido un error.
#include <sys/types.h> #include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
Pág. 18
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: listen()
- sockfd: descriptor del socket.
- backlog: número de conexiones permitidas en la cola de entrada.
- Resultado:
- “-1” si se ha producido un error.
#include <sys/socket.h>
int listen(int sockfd, int backlog);
Pág. 19
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: accept()
- sockfd: descriptor del socket.
- addr: puntero a una estructura struct sockaddr_in local, donde se
guardará la información de la conexión entrante y con ella se puede
averiguar qué máquina se está conectando, y desde qué puerto.
- addrlen: longitud de la estructura anterior.
- Resultado:
- Devuelve un descriptor de un socket conectado al cliente.
- “-1” si se ha producido un error.
- NOTA: se realiza en los programas servidores:
- Sockets pasivos.
- Quedan a la espera de recibir peticiones de conexión.
#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
Pág. 20
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: ejemplo de accept()
#include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h>
#define MYPORT 3490 /* Puerto al que se conectarán los clientes / #define BACKLOG 10 / Número de conexiones que vamos a mantener en cola */
void main(){ int sockfd, new_fd; /* Se escucha sobre sock_fd, nuevas conexiones sobre new_fd / struct sockaddr_in my_addr; / Información sobre mi dirección / struct sockaddr_in their_addr; / Información sobre la dirección remota */ int sin_size; sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET; /* Ordenación de la máquina / my_addr.sin_port = htons(MYPORT); / Ordenación de la red */ my_addr.sin_addr.s_addr = htonl(INADDR_ANY); memset(&(my_addr.sin_zero), '\0', 8); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); listen(sockfd, BACKLOG); sin_size = sizeof(struct sockaddr_in); new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); …
Pág. 21
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: send()
- sockfd: descriptor del socket.
- msg: puntero a los datos que se quieren enviar.
- len: longitud de los datos en octetos.
- flags: normalmente a 0.
- Resultado:
- Número de octetos enviados. (¡¡¡ Puede ser menor que len!!!)
- “-1” si se ha producido un error.
- Es el método de envío en sockets stream.
#include <sys/types.h> #include <sys/socket.h>
int send(int sockfd, const void *msg, int len, int flags);
Pág. 24
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: recvfrom()
- sockfd: descriptor del socket.
- buf: el buffer donde se va a depositar la información leída.
- len: longitud máxima del buffer.
- flags: normalmente a 0.
- from: contiene información sobre la IP y puerto de la máquina de origen.
- fromlen: longitud de la estructura anterior.
- Resultado:
- Número de octetos recibidos.
- “0” la máquina remota a cerrado la conexión.
- “-1” si se ha producido un error.
- Es el método de recepción en sockets datagram.
- NOTA: si usas socket datagram pero has realizado un connect() se puede utilizar el método recv.
#include <sys/types.h> #include <sys/socket.h>
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
Pág. 25
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: otras llamadas
lectura/escritura
• Existen otras llamadas equivalentes:
- int sendmsg(int sockfd, const struct msghdr *msg,
int flags)
- int recv(int s, struct msghdr *msg, int flags)
• Se puede utilizar también los métodos de escritura y lectura de
E/S:
- int write(socket, buffer, length)
- int read(socket, buffer, length)
Pág. 26
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: close()
- sockfd: descriptor del socket.
- Si se cierra un socket con close el otro extremo cuando intente leer o
escribir de ese socket recibirá un error. Para tener más control y realizar
por ejemplo un “half-close” es necesario emplear la llamada:
int close(sockfd);
int shutdown(int sockfd, int how);
- sockfd: descriptor del socket.
- how: toma uno de los siguientes valores:
- “0” no se permite recibir más datos.
- “1” no se permite enviar más datos.
- “2” no se permite enviar ni recibir más datos (lo mismo que close()).
Pág. 27
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: llamadas auxiliares
- sockfd: descriptor del socket.
- addr: puntero a una estructura sockaddr que guardará la información acerca del otro lado de la conexión.
- addrlen: longitud de la estructura anterior.
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, int *addrlen);
- sockfd: descriptor del socket.
- addr: puntero a una estructura sockaddr que guardará la información acerca del extremo local de la conexión.
- addrlen: longitud de la estructura anterior.
Pág. 30
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: llamadas auxiliares
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; #define h_addr h_addr_list[0]
- h_name : nombre oficial de la máquina.
- h_aliases : vector [ array ] terminado en NULL de nombres alternativos de máquina.
- h_addrtype : tipo de la dirección que se devuelve; usualmente AF_INET.
- h_length : longitud de la dirección en octetos.
- h_addr_list : un vector terminado en cero de direcciones de red de la máquina.
- h_addr : la primera dirección de h_addr_list.
Pág. 31
PROGRAMACIÓN CON SOCKETS - API DE SOCKETS
API de socket: llamadas auxiliares
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
int main(int argc, char *argv[]){ struct hostent *h;
if (argc != 2) { // Comprobación de errores en la línea de comandos fprintf(stderr,"usage: getip address\n"); exit(1); } if ((h=gethostbyname(argv[1])) == NULL) { // Obtener información del host herror("gethostbyname"); exit(1); } printf("Host name : %s\n", h->h_name); printf("IP Address : %s\n", inet_ntoa(*((struct in_addr *)h->h_addr))); return 0; }
Pág. 32
PROGRAMACIÓN CON SOCKETS - ESQUEMAS
Cliente TCP
1. Averiguar la dirección IP y puerto del servidor con el que desea
comunicarse.
2. Crear un socket: llamada a socket()
3. Especificar que TCP use cualquier puerto local libre.
- Lo hace automáticamente el connect() si no se hace previamente
un bind().
4. Conectar con el servidor.
- Esto abre la conexión TCP: llamada a connect().
5. Comunicarse con el servidor
- Enviar y recibir por el socket (llamadas a send/recv, write/read,
6. Cerrar el socket: llamada a close().
Pág. 33
PROGRAMACIÓN CON SOCKETS - ESQUEMAS
Servidor TCP
1. Crear un socket: llamada a socket().
2. Asociarlo al puerto local “bien conocido” que ofrece ese servicio:
llamada a bind().
3. Poner el socket en modo pasivo, dispuesto a aceptar peticiones
de conexión: llamada a listen().
4. Bucle:
- Recibir petición de cliente.
- Atender petición (por nuevo socket): llamada a accept()
- Enviar/recibir por socket (llamadas a send/recv, write/read, ...).
- Cerrar nuevo socket (llamada a close()) y volver a punto 3.
5. Cerrar socket: llamada a close().
Pág. 36
PROGRAMACIÓN CON SOCKETS - ESQUEMAS
Servidor TCP concurrente
1. Crear un socket.
2. Asociarlo al puerto local “bien conocido” que ofrece ese servicio.
3. Poner el socket en modo pasivo, dispuesto a aceptar peticiones
de conexión.
4. Cuando recibe petición de cliente:
- Crea un nuevo proceso hijo:
- Le pasa el nuevo socket para atender esa petición.
- Vuelve al punto 3.
5. Cerrar socket.
6. En paralelo, el proceso esclavo interactúa con el cliente.
- Enviar/recibir por socket.
- Cierra el socket y termina.