Redes Remotas 02 PROG REDES I

Transcripción

Redes Remotas 02 PROG REDES I
PROGRAMACION EN REDES
Sockets: Son una forma de comunicarse con otros programas usando descriptores de archivo estándar de
Unix.
En Unix todo es un archivo. Cuando los programas hacen cualquier operación de E/S, lo hacen escribiendo o
leyendo un descriptor de archivo (entero asociado a un archivo abierto). Existen dos llamadas básicas
send() y recv() las que ofrecen un gran control sobre la transmisión de datos.
Sockets de Internet:
Hay varios tipos:
1) RAW: Sockets puros.
2) SOCK_STREAM: Sockets de flujo (TCP, fiables y orientado a conexión, TCP: RFC-793 e IP: RFC791)
3) SOCK_DGRAM: Sockets datagramas (UDP, no fiables y no orientado a conexión, RFC-768)
Structs y manipulación de datos:
Estructura que mantiene información de direcciones para varios tipos de socket:
struct sockaddr {
unsigned short
char
protocolo
};
sa_family;
sa_data[14];
// familia de direcciones, AF_xxx
// 14 bytes de la dirección del
sa_family admite varios valores, AF_INET
sa_data contiene una dirección y número de puerto de destino para el socket.
Los programadores han creado una estructura paralela por Internet:
// Dirección de Internet (una estructura por herencia histórica)
struct in_addr {
unsigned long s_addr; // Esto es un long de 32 bits, ó 4 bytes
};
struct sockaddr_in {
short int
sin_family;
AF_INET
unsigned short int sin_port;
struct in_addr
sin_addr;
unsigned char
sin_zero[8];
tamaño original de struct sockaddr
};
_in de “Internet”
// familia de direcciones,
// Número de puerto
// Dirección de Internet
// Relleno para preservar el
Tratamiento de direcciones IP
Suponga que tiene una estructura “dire” de tipo “sockaddr_in” y se desea guardar la dirección IP
“10.20.20.190”:
dire.sin_addr.s_addr = inet_addr(“10.20.20.190”);
Para obtener o imprimir una IP:
printf("%s", inet_ntoa(dire.sin_addr)); //ntoa = network to ascii
Llamadas al sistema
Mediante algunas funciones, se permite el acceso a las características de red de una máquina Unix. Así el
núcleo toma el control y realiza el trabajo.
1) socket(): Consigue el descriptor de archivo.
int socket(int domain, int type, int protocol);
domain tiene que ser "AF_INET"
type le dice al núcleo qué tipo de socket es: SOCK_STREAM o SOCK_DGRAM
Por último, basta con asignar a protocol un "0" para que socket() elija el protocolo correcto en type
2) bind(): Obtiene el puerto. El núcleo usa este número para asociar los paquetes entrantes con un descriptor
de socket de un cierto proceso.
3) connect(): Conecta con una máquina remota (para pedir un servicio).
4) listen(): Espera a que lleguen conexiones de entrada para gestionarlas de alguna manera (brindar servicio)
5) accept(): Alguna IP intenta conectar con la máquina que tiene un puerto escuchando. Esta conexión pasará
a una cola, esperando a ser aceptada ( accept() ). Cuando se llama a accept() se le dice al sistema que
se quiere obtener una conexión pendiente. La llamada al sistema, a su vez, le devolverá un socket nuevo para
usarlo en esta nueva conexión. El original está todavía escuchando en el puerto, y el nuevo está listo para
enviar (send()) y/o recibir (recv()).
6) send() y recv():Estas dos funciones sirven para comunicarse a través de sockets de flujo.
7) sendto() y recvfrom(): Estas dos funciones sirven para comunicarse a través de sockets de datagramas.
Como en UDP no se establece conexión, se debe adjuntar la dirección de destino.
8) close() y shutdown(): Estas funciones impiden lecturas y/o escrituras al socket. Para mayor control, se
utiliza shutdown() que permite cerrar la comunicación en un cierto sentido, o en los dos.
9) getpeername(): Indica quién está al otro lado de un socket de flujo.
10) gethostname(): Indica el nombre del computador donde funciona el proceso.
11) DNS: En pocas palabras, se le indica la dirección de un sitio en forma humanamente legible y devuelve la
dirección IP (para usarla con bind() , connect(), sendto()).
Gethostbyname()
Preguntas más frecuentes
P: ¿Cómo se compilan los programas?
R: Se ejecuta la instrucción.
$ cc <prog.c> -o <nom_exe>
$ gcc <prog.c> -o <nom_exe>
P: ¿Cómo se ejecutan los programas?
R: Se ejecuta la instrucción.
$ ./<nom_exe>
P: ¿Qué hago cuando bind() responde "Address already in use" [La dirección ya se está usando]?
R: Significa que el puerto ya está ocupado, lo más práctico es cambiarlo en el código fuente.
P: ¿Cómo puedo obtener una lista de los sockets abiertos en el sistema?
R: Usa netstat . Revisa la página man para más detalles, un resultado directo sería:
$ netstat –na | Grep LIST | more
El truco consiste en averiguar qué socket está asociado con cual programa.
P: ¿Cómo puedo ejecutar el servidor y el cliente si solamente tengo un pc? ¿Se necesita una red para escribir
y probar programas de redes?
R: Afortunadamente no. Virtualmente todas las máquinas implementan un "dispositivo" de red de cierre de
circuito [loopback, localhost (127.0.0.1) dirección de pruebas] que reside en el núcleo y simula ser una tarjeta
de red. Se puede ejecutar el cliente en una consola virtual y el servidor en otra. O se inicia el servidor en
segundo plano [background] ("servidor &" ó "servidor", luego CTRL-Z y finalmente "bg") y luego
se ejecuta el cliente en la misma ventana.
En resumen, no se necesita modificar nada de código para funcione en una sola máquina sin conexión a la red.
P: ¿Cómo puedo saber si el sistema remoto ha cerrado la conexión?
R: Se sabe cuando recv() devuelve 0.
P: ¿Qué pasa al enviar un montón de datos, pero al aplicar recv(), sólo se recibe una cantidad bytes menor?
Sin embargo, si ejecuto en la máquina local se reciben todos los datos.
R: Está influyendo la MTU (Maximum Transfer Unit)--El tamaño máximo de paquete que el medio físico
puede manejar. En la máquina local se usa el “loopback” que puede manejar sin problemas 8K o más. Pero
una Ethernet existe un limite de bytes con una cabecera. Averiguar “encapsulación de datos” de TCP.
P: Los programas están tras un cortafuegos [firewall]--¿Cómo informo a los usuarios del otro lado del
cortafuegos cual es mi dirección IP para que puedan conectarse?
R: Desgraciadamente, la finalidad de un firewall es evitar que los usuarios del otro lado puedan conectarse,
así que permitirlo se considera en principio una brecha de seguridad.
Sin embargo, aún se pueden conectar a través del cortafuegos si éste realiza algún tipo de enmascaramiento
“masquerading” o NAT [Network Address Translation - Traducción de direcciones de red] o algo parecido.
Se deben diseñar los programas de modo que ellos siempre inicien la conexión. Si esta solución no funciona o
es muy compleja, se puede abrir un puerto en el cortafuegos para permitir conexiones.
Una solución mucho más peligrosa (por seguridad) es bajar el firewall. Se debe tener en cuenta que un agujero
en el cortafuegos es algo muy importante, y se debe estar muy seguro de no dar acceso a la red interna a
personas mal intencionadas.
Para deshabilitar el firewall se ejecuta la siguiente instrucción:
$ service ipchains stop
$ service iptables stop
$ service ip6tables stop
Dependiendo de la versión y distribución de Linux.

Documentos relacionados