Comunicación Entre Procesos Usando Sockets
Generalidades
Host Name (nombre de la máquina)
Nombre del host v/s dirección (mateo.elo.utfsm.cl v/s 200.1.17.4)Dirección del Host (dirección de la máquina)
Obtención del nombre del hostint gethostname(char *name, int len);Esta función pone el nombre del host local en el arreglo name, el cual es de largo len. Retorna 0 en caso de éxito y -1 en falla.
Dirección única en la Internet y de 32 bits en IPv4 (128 bits en IPv6). Otros nombres: dirección de red, dirección Internet.Transformación entre nombre y dirección de máquina
Usamos notación punto para referirnos a ellas. i.e. 123.45.67.89 a cambio de 0x7B2D4359
Cada dirección de red tiene dos partes: una dirección de subred y una de host dentro de la subred.
La dirección de red es usada por los routers para el enrutamiento de los paquetes en la red.
Clase B y C no resultaron adecuadas para los tamaños típicos de las organizaciones. Clase C (254 máquinas) no logra satisfacer las necesidades de muchas empresas medianas, y Clase B resulta muy grande para éstas (65.534 máquinas).
Las normas aprobadas para modificar la definición del espacio de direcciones se conocen bajo el nombre de Classless Inter-Domain Routing (CIDR) (Enrutamiento entre dominios sin usar clases), y los estándares que lo describen son: RFC 1467, RFC 1518, y RFC 1519.
Para hacer un mejor uso de las direcciones dentro de cada clase es posible definir subredes. Es decir, una clase se divide en varias subclases debiendo especificar cuántos bits corresponden a la dirección de la red (prefijo). La diferencia corresponde a la dirección de la máquina dentro de esa subred. Por ejemplo la red ELO podría tener:
Clase C: 200.1.17.0 dividida en dos subredes de 128 direcciones cada una.
#includeServicios y Números de Puertas
#include
#include
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
Estas funciones son la versión actual de las funciones gethostbyname() y getservbyname().
getaddrinfo()maneja transparentemente direcciones IPv4 e IPv6. Así el programa se torna independiente de la versión de IP que se ocupe.
struct addrinfo {
int ai_flags; /* input flags */
int ai_family; /* protocol family for socket */
int ai_socktype; /* socket type SOCK_STREAM, SOCK_DGRAM*/
int ai_protocol; /* protocol for socket */
socklen_t ai_addrlen; /* length of socket-address */
struct sockaddr *ai_addr; /* socket-address for socket */
char *ai_canonname; /* canonical name of host for service location */
struct addrinfo *ai_next; /* pointer to next in list */
};
La operación inversa (obtener nombre y servicio a partir de una dirección IP y puerto) se logra con getnameinfo()
Orden de los Bytes en la Red
Intel (80x86, Pentium y otros) y Digital Equipment Corporation (DEC) son little-endian. Motorola y Sun SPARC son big-endian (byte más significativo en la dirección menor de memoria).
Solución: se definió un orden de bytes de red (= big-endian). Así se asegura que el tráfico puede ser leído en ambas arquitecturas.
Funciones para hacer las conversiones:
#include <sys/types.h>gethostby* retornan enteros con orden de red.
#include <netinet/in.h>
u_long htonl (u_long hostlong);
u_short htons(u_short hostshort);
u_long ntohl(u_long netlong);
u_long ntohs(u_short netshort);
Ver páginas man en plataforma específica. Por ejemplo en aragorn año 2008, se tiene:
#include <netinet/in.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
Protocolos
Relevantes en Internet
(Las imágenes de esta sección fueron tomadas de: TCP/IP Illustrated, Volume 1. W. Richard Stevens)
Creación de
Sockets
El socket es la abstracción usada para definir un punto de comunicación.
#include <sys/types.h>domain especifica la familia de direcciones en que se debe interpretar la dirección del socket. Puede ser: AF_UNIX (Address Family UNIX), en cuyo caso la dirección es un pathname; o AF_INET (Address Family Internet), en caso de una dirección Internet IPv4, Usar AP_INET6 (Address Protocol Internet IPv6) para IPv6. Ver más en man socket.
#include <sys/socket.h>
int socket (int domain, int type, int protocol);
type puede ser SOCK_STREAM, lo cual especifica una conexión de circuito virtual. Es bidireccional y contínuo. En la Internet éste implica TCP. Otra opción es SOCK_DGRAM, lo cual especifica envío de paquetes discretos. En la Internet éste corresponde a UDP. Otras opciones son: SOCK_RAW para acceso a un protocolo "crudo", por ejemplo directo a IP , SOCK_RDM provee servicio garantizado pero sin comprometer orden.
protocolo es el número del protocolo específico a utilizar del tipo señalado. Normalmente sólo un protocolo es soportado de un tipo dado dentro de una familia de protocolos. Una opción es dejarlo en cero y el sistema resuelve el valor que corresponde a ese protocolo según el valor dado en type.
La función retorna un descriptor de socket. Éste es similar a un descriptor de archivo.
Funciones en programación de
Servidor
El servidor debe asociar el socket con una puerta y dirección de interfaz de red para que los clientes puedan acceder a él. Esto se logra con la función bind.
#include <sys/types.h>s es el socket ya creado,
#include <sys/socket.h>
int bind (int s, const sockaddr * name, int addrlen);
struct socketaddr_in {Las máquinas pueden tener más de una tarjeta de red; i.e. más de una dirección Internet, por ello se debe especificar la dirección sin_addr. Si queremos atender requerimientos entrando por cualquier interfaz de red, se puede usar INADDR_ANY como dirección asociada a la interfaz.
short sin_family; /* AF_INET */
u_short sin_port; /* puerta de servicio */
struct in_addr sin_addr; /* dirección asociada a la puerta */
}
Esperando por Conexiones
Sólo es requerido cuando el servicio es orientado al stream (TCP).
#include <sys/types.h>Esta función comunica al sistema operativo que el socket está listo para recibir conexiones. backlog especifica el máximo número de requerimientos de conexión que pueden estar pendientes en cada momento (<= 5). Si hay muchos tratando de conectarse, el cliente recibe un mensaje "connection refused".
#include <sys/socket.h>
int listen (int s, int backlog);
Aceptación de Conexiones
#include <sys/types.h>Esta función retorna un nuevo descriptor de socket que el servidor usa para comunicarse con el cliente que fue aceptado. El OS almacena la información del cliente en name y el largo de la estructura en addrlen; es decir son valores de salida.
#include <sys/socket.h>
int accept (int s, struct sockaddr *name, int *addrlen);
Funciones Usadas en lado Cliente
Conexión con el
servidor
#include <sys/types.h>Esta función conecta el socket s con el servidor corriendo en la máquina y puerto especificada en name.
#include <sys/socket.h>
int connect (int s, struct socketaddr *name, int addrlen);
Esta fucnión puede ser usada en conexiones datagramas o de circuito virtual. En el primer caso esta función deja claro que los próximos datagramas están dirigidos al destido donde se hace la conexión. De otra forma la dirección destino debe ser especificada en cada llamado de envío de datagrama.
Funciones para Transferencia de Datos (Servicor y Cliente)
#include <sys/types.h>Son idénticas a read and write, sólo que ellas tienen un cuarto argumento para especificar como enviar o recibir los datos.
#include <sys/socket.h>
int recv (int s, char * buf, int len, int flags);
int send (int s, const char * buf, int len, int flags);
Cuando se usa conexión datagrama, el servidor no llama a las funciones listen y accept, y el cliente generalemnet no llama a la función connect. En estos casos se usan las funciones:
int recvfrom(int s, char * buf, int len, int flags, struct sockaddr *from, int fromlen);Destrucción o Cierre del Canal de Comunicación (Servicor y Cliente)
int sendto(int s, const char *buf, int len, int flags, struct sockaddr *to, int tolen);
#include <sys/types.h>how = 0 se cierra para lectura, se puede seguir enviando.
#include <sys/socket.h>
int shutdown(int s, int how);
Ejemplos:
TCP: Servidor Cliente
UDP: Servidor Cliente