Tp 2 (BSD sockets)

Anuncio
66.48 Seminario de Redes
Trabajo Práctico Nº2
Programación de una aplicación distribuida
usando la API de Berkeley Sockets
Grupo: MOG
2º Cuatrimestre 2004
66.48 Seminario de Redes
MOG
Objetivo
Familiarizarse con la API (Application Programming Interface) de los Berkeley Sockets y
hacer uso de las system calls. Realizar simples aplicaciones distribuidas que pongan en
evidencia el funcionamiento de las mismas, haciendo hincapié en que el medio de transmisión
es no confiable.
Introducción
Luego de haber aprendido cómo funcionan los Protocolos TCP/IP, es necesario
analizar la interfaz que existe entre los programas de aplicación y el software de protocolo,
considerando el hecho de que los estándares no especifican exactamente cómo interactúan, y
por ende es que la arquitectura de interfaz no está estandarizada. Es más, como el software de
protocolo residen en el sistema operativo de una pc, los detalles de la interfaz dependen del
mismo.
Una ejemplo de interfaz entre los programas de aplicación y los protocolos TCP/IP, es
la abstracción denominada Socket. Dando un definición concreta, podría decirse que en UNIX
y otros Sistemas Operativos, es un objeto (entidad que consiste de datos y procedimientos para
manipular los mismos) de software que conecta una aplicación con el protocolo de red. El
Socket es el endpoint de una comunicación entre dos programas corriendo en la red. Si
pensamos al Socket como una generalización del mecanismo de acceso a archivos de UNIX,
los programas de aplicación requieren que el Sistema Operativo cree un Socket cuando se
necesita. El SO devuelve un entero que utiliza el programa para hacer referencia al Socket
recientemente creado. A diferencia con el acceso a archivos, puede crear Sockets sin enlazarlos
a direcciones de destino específicas. La aplicación puede elegir abastecer una dirección de
destino cada vez que utiliza el Socket (es decir, cuando se envían Datagramas UDP) o elegir
enlazar la dirección de destino a un Socket y evadir la especificación de destino repetidamente
(es el caso cuando se realiza una conexión TCP).
Los Sockets hacen ejecuciones iguales a los archivos de UNIX, de manera que pueden
utilizarse con operaciones tales como read y write. Un vez que una aplicación crea un Socket
y una conexión TCP del Socket a un destino externo, el programa puede hacer uso de write
para mandar un flujo de datos a través de la conexión (el programa de aplicación en el otro
extremo puede utilizar read para recibirlo).
Si tomamos un programa cliente que intenta establecer una comunicación con un
programa servidor, sería de la siguiente forma:
Normalmente, el servidor, que corre en una PC, posee un Socket al cual se le asocia un
número de Puerto. El servidor “escucha” el Socket esperando un pedido de conexión de un
cliente. El cliente conoce el hostname de la PC donde está corriendo el servidor y el número de
puerto donde se está ejecutando el servicio. Así, el cliente le hace un pedido de conexión al
servidor en la PC donde corre y al puerto que tiene asociado.
Página 1
66.48 Seminario de Redes
MOG
Creación de un Socket
El system call socket crea sockets cuando se lo pide. Toma tres argumentos enteros y
devuelve un resultado entero.
resultado = socket(pf, tipo, protocolo)
pf : especifica familia de protocolo que se va a utilizar, es decir, especifica cómo interpretar la
dirección cuando se suministra (para TCP/IP es PF_INET).
tipo: especifica el tipo de comunicación que se desea. SOCK_STREAM es para servicio de
entrega confiable de flujo (TCP), SOCK_DGRAM es para entrega de datagramas sin conexión
(UDP).
protocolo: se utiliza para acomodar los diversos protocolos dentro de una familia. Especifica
el protcolo en caso que el modelo de red soporte diferentes tipos de modelos de Stream y
Datagrama. Como TCP/IP sólo tiene uno para cada uno, se setea en 0.
Un ejemplo con TCP sería:
s = socket(PF_INET, SOCK_STREAM, 0)
Finalización del Socket
Cuando un proceso termina de utilizar un socket, se llama a:
close(socket)
El argumento socket especifica al descriptor (entero) de un socket para que cierre.
Especificación de una dirección local
Inicialmente, un socket se crea sin ninguna asociación hacia direcciones locales o de
destino. Para TCP/IP significa que ningún número de puerto de protocolo local se ha
asignado y que ningún puerto de destino o dirección IP se ha especificado. En muchos casos,
los programas de aplicación no se preocupan por las direcciones locales que utilizan, ni están
dispuestos a permitir que el software de protocolo elija una para ellos. Sin embargo los
procesos del servidor que operan en un puerto bien conocido deben ser capaces de especificar
dicho puerto para el sistema. Una vez que se ha creado un socket, el servidor utiliza una
llamada del sistema bind para establecer una dirección local para ello.
bind(socket, localaddr, addrlen)
Página 2
66.48 Seminario de Redes
MOG
socket: descriptor de enteros del socket que ha de ser enlazado
localaddr: estructura que especifica la dirección local a la que el socket deberá enlazarse
addrlen: entero que especifica la longitud de las direcciones medidas en octetos.
Estructura de las direcciones (sockaddr):
struct sockaddr {
u_short sa_family;
char
sa_data[14];
};
0
/* address family */
/* up to 14 bytes of direct address */
16
31
Familia de Direcciones
Octetos de Dirección 0-1
Octetos de Dirección 2-5
Octetos de Dirección 6-9
Octetos de Dirección 10-13
Familia de Direcciones (16 bits) identifica el conjunto de protocolos al que pertenece la
dirección. Seguido de una dirección de hasta 14 octetos. El valor de este campo determina el
formato de los octetos restantes. El valor 2 significa que posee una dirección de TCP/IP, y la
dirección de socket se conoce como sockaddr_in. Esto incluye una dirección del IP y un
número de puerto de protocolo. Formato exacto para TCP/IP:
struct sockaddr_in {
short
sin_family;
u_short sin_port;
struct in_addr sin_addr;
char
sin_zero[8];
};
donde in_addr es:
typedef struct in_addr {
union {
struct {
u_char s_b1,s_b2,s_b3,s_b4;
} S_un_b;
struct {
u_short s_w1,s_w2;
} S_un_w;
u_long S_addr;
} S_un;
} in_addr;
0
16
Familia de Direcciones (2)
S_un
S_un_b
Address of the host formatted as four u_chars.
S_un_w
Address of the host formatted as two u_shorts.
S_addr
Address of the host formatted as a u_long.
31
Puerto de Protocolo
Dirección IP
CERO
CERO
Página 3
66.48 Seminario de Redes
MOG
Si bien la estructura se podría llenar con valores arbitrarios; en el caso de que, por
ejemplo, quien llame podría pedir un puerto de protocolo local que ya está en uso por otro
programa, o si pidiese una dirección IP no válida entonces bind falla y devuelve un código de
error.
Conexión de socket (para TCP)
Inicialmente, un socket se crea en el unconnected state (estado no conectado), lo que
significa que el socket no está asociado con ningún destino externo. La llamada de sistema
connect (conectar) enlaza un destino permanente a un socket, colocándolo en el connected
state (estado conectado). Una aplicación debe llamar a connect para establecer una conexión
antes de que pueda transferir datos a través de un socket de flujo confiable. Los sockets
utilizados con servicios de datagrama sin conexión necesitan no estar conectados antes de
usarse, pero haciéndolo así, es posible transferir datos sin especificar el destino en cada
ocasión.
connect(socket, destaddr, addrlen)
socket: descriptor entero del socket que ha de conectarse
destaddr: es una estructura de dirección de socket en la que se especifica la dirección de
destino a la que deberá enlazarse el socket.
addrlen: longitud de la dirección de destino medida en octetos.
En el caso de servicio sin conexión, connect no hace nada más que almacenar localmente la
dirección de destino.
Envío de datos
Hay cinco system calls posibles, una vez que se ha establecido el socket,
write(socket, buffer, length)
buffer: contiene la dirección de datos que se han de mandar
length: número de octetos que se han de mandar
send(socket, message, length, flags)
message: da la dirección de datos a ser mandados
length: especifica el número de octetos que se va a enviar
flags: controla la transmisión
Las llamadas de sistema sendto y sendmsg permiten que quien llama envíe un mensaje
a través de un socket no conectado, pues ambos requieren que quien llama especifique un
destino. Sendto, que toma la dirección de destino como argumento, tiene la forma
Página 4
66.48 Seminario de Redes
MOG
sendto(socket, message, length, flags, destaddr, addrlen)
Los dos argumentos finales especifican una dirección de destino y dan la longitud de la
misma. destaddr utiliza la estructura sockadrr_in mencionada anteriormente.
Se puede utilizar sendmsg en casos en los que la larga lista de argumentos requeridos para
sendto haga que el programa sea ineficaz o difícil de leer.
sendmsg(socket, messagestruct, flags)
messagestruct: es una estructura que contiene información acerca del mensaje, longitud del
mismo, dirección de destino y su longitud.
APUNTADOR A SOCKETADOR
TAMAÑO DE SOCKETADOR
APUNTADOR DE LISTA IOVEC
LONGITUD DE LISTA IOVEC
APUNTADOR DE LISTA DE DERECHOS DE ACCESO
LONGITUD DE LISTA DE DERECHOS DE ACCESO
A su vez está writev que no describiremos y conforma la quinta system call.
Recepción de datos
Ofrece cinco llamadas de sistema, análogas a las cinco operaciones de salida, para
utilizar en la recepción de datos a través de un socket.
read sólo se puede usar cuando un socket se conecta.
read(descriptor, buffer, length)
descriptor: entero de un socket del que se puede leer los datos
buffer: especifica la dirección de memoria donde se almacenan los datos
length: especifica el número máximo de octetos que pueden leerse
La llamada recvfrom permite la entrada de un socket no conectado.
recvfrom(socket, buffer, length, flags, fromaddr, addrlen)
Los dos argumentos adicionales:
fromaddr y addrlen son apuntadores de estructura de dirección de socket y un entero. El
sistema operativo utiliza fromaddr para registrar las direcciones de quien envía el mensaje y
utiliza el fromlen para registrar la longitud de la dirección de quien lo envía.
recvmsg, es análoga a la operación de salida sendmsg. Opera como recvfrom, pero requiere
menos argumentos.
Página 5
66.48 Seminario de Redes
MOG
recvmsg(socket, messagestruct, flags)
donde el argumento messagestruct da la dirección de una estructura que sostiene la
dirección para el mensaje que entra así como también las ubicaciones para la dirección de
quien envía. La forma es la misma que en la de sendmsg.
Para completar las cinco system calls están readv y recv.
Obtención de direcciones de socket
Un proceso llama a getpeername para determinar la dirección de la pareja (el extremo
remoto) al que se conecta el socket.
getpeername(socket, destaddr, addrlen)
destaddr: es un apuntador de estructura de tipo sockaddr que recibirá la dirección de socket.
addrlen: es un apuntador de entero que recibirá la longitud de la dirección.
Sólo trabaja con socket conectados.
getsockname devuelve la dirección local asociada con un socket
getsockname(socket, localaddr, addrlen)
localaddr: apuntador a una estructura de tipo sockaddr que contendrá la dirección;
addrlen: apuntador a entero que contendrá la longitud de dirección.
Especificación de una longitud de cola para un servidor
Un servidor crea un socket, lo enlaza a un puerto de protocolo bien conocido y espera
las peticiones. Si el servidor emplea entrega de flujo confiable, o si la computación de una
respuesta lleva cantidades no triviales de tiempo, puede suceder que una nueva petición llegue
antes de que el servidor termine de responder a un petición anterior. Para ello existe una
llamada, que deja en cola la peticiones hasta poder procesarlas:
listen(socket, qlength)
Pone al socket en modo pasivo listo para aceptar las conexiones. También informa al
sistema operativo de poner en cola las peticiones posteriores.
qlength: especifica la longitud de la cola de espera para ese socket. Si la cola está llena, el
sistema rechaza la conexión descartando la petición. Sólo para el servicio de entrega de flujo
confiable.
Servidor aceptando conexiones
Página 6
66.48 Seminario de Redes
MOG
Recordemos que el socket no está conectado a un destino exterior específico. De
hecho, ese destino exterior debe ser un wildcard (comodín), lo cual permite que el socket
reciba peticiones de conexión de cualquier cliente.
Una vez establecido un socket, el servidor espera la conexión. Para ello se utiliza accept. Una
llamada accept se bloquea hasta que la petición de conexión llega.
newsock = accept(socket, addr, addrlen)
Cuando llega una petición, el sistema llena un argumento addr con la dirección del
cliente que ha colocado la petición y configura addrlen a la longitud de la dirección. Por
último, crea un nuevo socket que tiene su destino conectado hacia el cliente que pide, y
devuelve el nuevo descriptor de socket a quien llama. Cuando llega una petición de conexión la
llamada accept reaparece. El servidor puede manejar las peticiones de manera concurrente
(después de que la llamada accept reapareció, el servidor maestro crea un esclavo para
manejar la petición. El proceso esclavo hereda una copia del nuevo socket, de modo que puede
proceder a servir la petición. Cuando finaliza, el esclavo cierra el socket y termina. El proceso
maestro cierra su copia de un nuevo socket y después de iniciar al esclavo. Después llama a
accept para obtener la siguiente petición de conexión.) o iterativa (el servidor mismo maneja
la petición, cierra el nuevo socket y, después, llama a accept para obtener la siguiente
petición).
Obtención y especificación de nombre de host
El sistema UNIX mantiene un nombre de host interno. Para las máquinas de Internet,
el nombre interno suele elegirse como el nombre de dominio para la interfaz principal de la red
de la máquina. gethostname permite a los procesos del usuario que accedan al nombre de
host, y la llamada sethostname permite a los procesos privilegiados definir el nombre de host.
gethostname(name, length)
name: da la dirección de un arreglo de octetos en la que se ha de almacenar el nombre
length: entero que especifica la longitud del arreglo name.
sethostname(name, length)
Los argumentos son análogos.
Página 7
66.48 Seminario de Redes
MOG
Desarrollo
Debido a que la escasez de tiempo presentada por las diversas circunstancias que afectaron
al normal desarrollo del seminario nos replanteamos el objetivo de programar una aplicación
distribuida más compleja con protocolos propietarios y más funcionalidades. En cambio,
optamos por una aplicación de mayor simplicidad a modo de “API demo”.
La función principal de la aplicación es la de realizar un “file transfer” desde un cliente
hacia un servidor por dos métodos distintos:
o conexión TCP: se encarga de garantizar el transporte.
o conexión UDP: a través un simple protocolo propietario STOP&WAIT.
Funcionamiento y Sintaxis de Uso
El funcionamiento básico de la aplicación es la siguiente. Primero debe ejecutarse en modo
servidor especificando el transporte, puerto y nombre de archivo a escribir los datos entrantes.
Segundo, debe ejecutarse el cliente de manera que aparee la configuración del servidor. Una
vez terminada la transferencia del archivo, ambos programas se cierran automáticamente ya
que poseen un mecanismo para darse cuenta cuando finalizó la misma.
El archivo ejecutable que se adjuntará junto con el código fuente se llama mogsocket
y su sintaxis es la siguiente:
Modo Servidor
./mogsocket [udp|tcp] server port filename
Modo Cliente
./mogsocket [udp|tcp] client IP port filename chunksize
[udp|tcp]: protocolo de transporte a elegir.
server/client: modo en que funcionará el programa.
IP: (sólo en modo cliente) dirección IP del servidor al que me quiero conectar.
port: puerto en que escuchará o mandará datagramas dependiendo del rol de la aplicación.
filename: nombre del archivo a leer(cliente) o escribir(servidor).
chunksize: (sólo en modo cliente) tamaño de escritura en el socket. En el caso de transporte
UDP será el tamaño del datagrama.
Análisis de Capturas
A la hora de realizar las capturas estas se prefirieron hacer sobre Internet o sobre una
Internet simulada mediante un “traffic shaper”, ya que sobre una LAN no se evidencian, en
general, las características del protocolo IP. Esta consideración es muy importante y
generalmente olvidada por gran parte de los programadores, ya que sus aplicaciones suelen
correr sin problemas en un entorno de LAN. Sin embargo, cuando lo hacen sobre Internet
surgen problemas no previstos como la pérdida, duplicación, pérdida de orden, gran variación
de latencia y etc. de los datagramas que causan fallas en aplicación o peor aún su total
inutilización debido a loop infinitos o sentencias blocantes.
Página 8
66.48 Seminario de Redes
MOG
Captura con Bw 100Kbps en TCP
Esta prueba se realizó con un “traffic shaper” para contrastar contra la anterior captura,
exceptuando que no hay perdida de paquetes aleatoria, sólo por overflow de colas(en el “traffic
shaper”) o buffers(en la aplicación).
Los comandos utilizados para simular un medio de transmisión de 100Kbps con una
cola de 5KBytes fueron los siguientes.
ipfw pipe 1 config bw 100Kbit/s queue 5KBytes
ipfw pipe 2 config bw 100Kbit/s queue 5KBytes
ipfw add 101 pipe 1 ip from 192.168.2.84 to 192.168.2.99 in
ipfw add 102 pipe 2 ip from 192.168.2.99 to 192.168.2.84 out
Las primeras dos líneas definen los pipes en ambos sentidos de transmisión y las segundas dos
relacionan los pipes con pares de direcciones IP.
Se ejecutaron las siguientes líneas de comando.
192.168.2.99 – TTY01 #tcpdump -i rl0 -s 0 tcp port 1024
192.168.2.99 – TTY02 #./mogsocket tcp client 192.168.2.84 1024 posta02 1000
192.168.2.84#./mogsocket server 1024 arch4
Tramas
No.
Time
1 0.000000
Source
192.168.2.99
2 0.000985
192.168.2.84
3 0.017882
192.168.2.99
4 0.097887
192.168.2.99
5 0.099451
192.168.2.84
6 0.217916
192.168.2.99
7 0.218898
192.168.2.84
8 0.337923
192.168.2.99
9 0.338861
192.168.2.84
10 0.457915
192.168.2.99
11 0.458857
192.168.2.84
12 0.577918
192.168.2.99
13 0.578809
192.168.2.84
14 0.697924
192.168.2.99
15 0.698878
192.168.2.84
16 0.817933
192.168.2.99
17 0.819002
192.168.2.84
18 0.937924
192.168.2.99
19 0.938762
192.168.2.84
20 1.057922
192.168.2.99
21 1.058751
192.168.2.84
22 1.137915
192.168.2.99
Destination
192.168.2.84
Protocol
TCP
Info
1028 > 1024 [SYN] Seq=0 Ack=0 Win=65535 Len=0
MSS=1460 WS=1 TSV=17075044 TSER=0
192.168.2.99
TCP
1024 > 1028 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0
MSS=1460 TSV=1333473 TSER=17075044 WS=0
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=1 Ack=1 Win=66608 Len=0
TSV=17075045 TSER=1333473
192.168.2.84
TCP
1028 > 1024 [PSH, ACK] Seq=1 Ack=1 Win=66608
Len=1000 TSV=17075045 TSER=1333473
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=1001 Win=7000 Len=0
TSV=1333483 TSER=17075045
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=1001 Ack=1 Win=66608
Len=1448 TSV=17075045 TSER=1333473
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=2449 Win=10136 Len=0
TSV=1333495 TSER=17075045
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=2449 Ack=1 Win=66608
Len=1448 TSV=17075046 TSER=1333473
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=3897 Win=13032 Len=0
TSV=1333507 TSER=17075046
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=3897 Ack=1 Win=66608
Len=1448 TSV=17075046 TSER=1333473
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=5345 Win=15928 Len=0
TSV=1333519 TSER=17075046
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=5345 Ack=1 Win=66608
Len=1448 TSV=17075054 TSER=1333483
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=6793 Win=18824 Len=0
TSV=1333536 TSER=17075054
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=6793 Ack=1 Win=66608
Len=1448 TSV=17075067 TSER=1333495
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=8241 Win=21720 Len=0
TSV=1333548 TSER=17075067
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=8241 Ack=1 Win=66608
Len=1448 TSV=17075090 TSER=1333519
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=9689 Win=24616 Len=0
TSV=1333560 TSER=17075090
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=9689 Ack=1 Win=66608
Len=1448 TSV=17075103 TSER=1333536
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=11137 Win=27512 Len=0
TSV=1333571 TSER=17075103
192.168.2.84
TCP
1028 > 1024 [ACK] Seq=11137 Ack=1 Win=66608
Len=1448 TSV=17075103 TSER=1333536
192.168.2.99
TCP
1024 > 1028 [ACK] Seq=1 Ack=12585 Win=30408 Len=0
TSV=1333583 TSER=17075103
192.168.2.84
TCP
1028 > 1024 [FIN, PSH, ACK] Seq=12585 Ack=1
Página 9
66.48 Seminario de Redes
MOG
23 1.139602
192.168.2.84
192.168.2.99
TCP
24 1.147851
192.168.2.99
192.168.2.84
TCP
Win=66608 Len=979 TSV=17075114 TSER=1333548
1024 > 1028 [FIN, ACK] Seq=1 Ack=13565 Win=33304
Len=0 TSV=1333591 TSER=17075114
1028 > 1024 [ACK] Seq=13565 Ack=2 Win=66608 Len=0
TSV=17075158 TSER=1333591
Las tramas 1, 2 y 3 son el conocido three-way handshake de TCP para establecer la
conexión. Puede verse que presenta opciones como:
o MSS: Maximum Segment Size es el máximo tamaño que puede mandar y por ende
recibir sin que haya fragmentación IP
o TimeStamp Option: que consta de dos campos timestamp value y echo reply (tsval y
tser)
o WS: Window Scale es un multiplicador (bit shift para el caso) para poder ir más allá de
los 65535 bytes de tamaño que darían los 16 bits.
Como se puede observar la transferencia se realizó sin problemas.
Resumiendo en números:
Nodo A
192.168.2.84 port: 1024
Nodo B
192.168.2.99 port: 1028
Total Packets
24
Total Bytes
15163
A-> B Packets
11
A-> B Bytes:
734
B->A Packets:
13
B->A Bytes
14429
De estos números se puede verificar el ancho de banda restringido por el “traffic
shaper” que da:
14429 ⋅ 8
BwB −> A estimado =
100,5Kbps
1,147851
Captura con Bw 100Kbps y Packet Loss Rate 0,5 en TCP
Como la Internet de hoy en día a mejorado en confiabilidad debido al crecimiento
tecnológico, tuvimos que realizar la prueba con un “traffic shaper”. Para lograr dicho objetivo
se utilizó una máquina con un sistema operativo FreeBSD versión 4.5 con el kernel compilado
con la opción DUMMYNET que es la que permite la funcionalidad de traffic shaping.
Los comandos utilizados para simular un medio de transmisión de 100Kbps con una
cola de 5KBytes y un packet loss rate de 0,5 (pierde un 50% de los paquetes) fueron los
siguientes.
ipfw pipe 1 config bw 100Kbit/s queue 5KBytes plr 0.5
ipfw pipe 2 config bw 100Kbit/s queue 5KBytes plr 0.5
ipfw add 101 pipe 1 ip from 192.168.2.84 to 192.168.2.99 in
ipfw add 102 pipe 2 ip from 192.168.2.99 to 192.168.2.84 out
Página 10
66.48 Seminario de Redes
MOG
Las primeras dos líneas definen los pipes en ambos sentidos de transmisión y las segundas dos
relacionan los pipes con pares de direcciones IP.
Se ejecutaron las siguientes líneas de comando.
192.168.2.99 – TTY01 #tcpdump -i rl0 -s 0 tcp port 1024
192.168.2.99 – TTY02 #./mogsocket tcp client 192.168.2.84 1024 arch1 1000
192.168.2.84#./mogsocket server 1024 save1
No.
Time
1 0.000000
Source
192.168.2.99
Destination
192.168.2.84
2 0.001468
192.168.2.84
192.168.2.99
3 0.009842
192.168.2.99
192.168.2.84
4 0.139787
192.168.2.99
192.168.2.84
5 0.140776
192.168.2.84
192.168.2.99
6 1.119821
192.168.2.99
192.168.2.84
7 1.120821
192.168.2.84
192.168.2.99
8 3.119816
192.168.2.99
192.168.2.84
9 3.136586
192.168.2.84
192.168.2.99
10 31.120239
192.168.2.99
192.168.2.84
11 31.122358
192.168.2.84
192.168.2.99
12 31.240221
192.168.2.99
192.168.2.84
13 31.241438
192.168.2.84
192.168.2.99
14 46.230474
192.168.2.99
192.168.2.84
15 46.231505
192.168.2.84
192.168.2.99
16 94.231172
192.168.2.99
192.168.2.84
17 94.232399
192.168.2.84
192.168.2.99
18 158.232153
192.168.2.99
192.168.2.84
19 158.233765
192.168.2.84
192.168.2.99
20 158.362142
192.168.2.99
192.168.2.84
21 158.363269
192.168.2.84
192.168.2.99
22 158.422117
192.168.2.99
192.168.2.84
23 158.423811
192.168.2.84
192.168.2.99
24 158.637643
192.168.2.84
192.168.2.99
25 158.732123
192.168.2.99
192.168.2.84
26 159.632159
192.168.2.99
192.168.2.84
27 159.632670
192.168.2.84
192.168.2.99
Protocol Info
TCP
1034 > 1024 [SYN] Seq=0 Ack=0 Win=65535 Len=0
MSS=1460 WS=1 TSV=17693853 TSER=0
TCP
1024 > 1034 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0
MSS=1460 TSV=1952157 TSER=17693853 WS=0
TCP
1034 > 1024 [ACK] Seq=1 Ack=1 Win=66608 Len=0
TSV=17693854 TSER=1952157
TCP
1034 > 1024 [ACK] Seq=1 Ack=1 Win=66608 Len=1448
TSV=17693855 TSER=1952157
TCP
1024 > 1034 [ACK] Seq=1 Ack=1449 Win=8688 Len=0
TSV=1952171 TSER=17693855
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1 Ack=1
Win=66608 Len=1448 TSV=17693954 TSER=1952157
TCP
[TCP Dup ACK 5#1] 1024 > 1034 [ACK] Seq=1
Ack=1449 Win=8688 Len=0 TSV=1952271 TSER=17693954
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1 Ack=1
Win=66608 Len=1448 TSV=17694154 TSER=1952157
TCP
[TCP Dup ACK 5#2] 1024 > 1034 [ACK] Seq=1
Ack=1449 Win=8688 Len=0 TSV=1952468 TSER=17694154
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1 Ack=1
Win=66608 Len=1448 TSV=17696954 TSER=1952157
TCP
[TCP Dup ACK 5#3] 1024 > 1034 [ACK] Seq=1
Ack=1449 Win=8688 Len=0 TSV=1955268 TSER=17696954
TCP
1034 > 1024 [ACK] Seq=1449 Ack=1 Win=66608
Len=1448 TSV=17696965 TSER=1955268
TCP
1024 > 1034 [ACK] Seq=1 Ack=2897 Win=11584 Len=0
TSV=1955280 TSER=17696965
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1449
Ack=1 Win=66608 Len=1448 TSV=17698465 TSER=1955268
TCP
[TCP Dup ACK 13#1] 1024 > 1034 [ACK] Seq=1
Ack=2897 Win=11584 Len=0 TSV=1956777 TSER=17698465
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1449
Ack=1 Win=66608 Len=1448 TSV=17703265 TSER=1955268
TCP
[TCP Dup ACK 13#2] 1024 > 1034 [ACK] Seq=1
Ack=2897 Win=11584 Len=0 TSV=1961581 TSER=17703265
TCP
[TCP Out-Of-Order] 1034 > 1024 [ACK] Seq=1449
Ack=1 Win=66608 Len=1448 TSV=17709665 TSER=1955268
TCP
[TCP Dup ACK 13#3] 1024 > 1034 [ACK] Seq=1
Ack=2897 Win=11584 Len=0 TSV=1967975 TSER=17709665
TCP
1034 > 1024 [ACK] Seq=2897 Ack=1 Win=66608
Len=1448 TSV=17709677 TSER=1967975
TCP
1024 > 1034 [ACK] Seq=1 Ack=4345 Win=14480 Len=0
TSV=1967988 TSER=17709677
TCP
1034 > 1024 [FIN, PSH, ACK] Seq=4345 Ack=1
Win=66608 Len=676 TSV=17709677 TSER=1967975
TCP
1024 > 1034 [FIN, ACK] Seq=1 Ack=5022 Win=17376
Len=0 TSV=1967994 TSER=17709677
TCP
[TCP Retransmission] 1024 > 1034 [FIN, ACK] Seq=1
Ack=5022 Win=17376 Len=0 TSV=1968015 TSER=17709677
TCP
1034 > 1024 [ACK] Seq=5022 Ack=2 Win=66608 Len=0
TSV=17709726 TSER=1968015
TCP
[TCP Dup ACK 25#1] 1034 > 1024 [ACK] Seq=5022
Ack=2 Win=66608 Len=0 TSV=17709816 TSER=1968015
TCP
1024 > 1034 [RST] Seq=2 Ack=1680971182 Win=0
Len=0
Se puede observar la retransmisión debido a la pérdida de tramas. También como
curiosidad nos sorprendió que el cliente ya había ejecutado todos los writes y por ende retornó
el prompt de shell. Sin embargo, por las capturas podemos observar que el kernel intentaba
seguir transmitiendo los datos. Probablemente porque tenía baja prioridad ante otros procesos
no tomaba algunos acknowledges que le llegan y por eso retransmitía.
Página 11
66.48 Seminario de Redes
MOG
Captura con Bw 100Kbps y Packet Loss Rate 0,5 en UDP
Con el mismo argumento de la prueba anterior optamos por utilizar un “traffic shaper”. Para
eso utilizamos la misma configuración de pipes descripta anteriormente.
Se ejecutaron las siguientes líneas de comando.
192.168.2.99 – TTY01 #tcpdump -i rl0 -s 0 udp port 1024
192.168.2.99 – TTY02 #./mogsocket udp server 1024 save1
192.168.2.84#./mogsocket udp client 192.168.2.99 1024 arch1 1000
Tramas
No.
Î
Î
Í
Î
Î
Î
Î
Î
Í
Î
Î
Í
Î
Î
Î
Î
Î
Í
Î
Î
Î
Î
Î
Í
Î
Í
Î
Î
Î
Î
Î
Î
Î
Î
Î
Î
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Time
0.000000
2.006712
2.093615
2.097853
4.057558
6.072149
8.083996
10.096995
10.180524
10.181497
12.161879
12.254782
12.256070
14.233200
16.257978
18.229401
20.242467
20.324155
20.325670
22.335772
24.308828
26.323614
28.344918
28.424324
28.426255
28.430859
28.431701
30.399381
32.420959
34.403851
36.406986
38.420341
40.433268
42.396343
44.409189
46.412499
Source
192.168.2.84
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
192.168.2.84
Destination
192.168.2.99
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
192.168.2.99
Protocol
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
Info
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
32770
32770
1024
32770
32770
32770
32770
32770
1024
32770
32770
1024
32770
32770
32770
32770
32770
1024
32770
32770
32770
32770
32770
1024
32770
1024
32770
32770
32770
32770
32770
32770
32770
32770
32770
32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Dst.port: 1024
Seq#
1000
1000
1000
2000
2000
2000
2000
2000
2000
3000
3000
3000
4000
4000
4000
4000
4000
4000
5000
5000
5000
5000
5000
5000
5020
5020
-1
-1
-1
-1
-1
-1
-1
-1
-1
-1
Type
data
data
ACK
data
data
data
data
data
ACK
data
data
ACK
data
data
data
data
data
ACK
data
data
data
data
data
ACK
data
ACK
EOF
EOF
EOF
EOF
EOF
EOF
EOF
EOF
EOF
EOF
En rojo se marcaron las retransmisiones. Se puede observar que hay 23
retransmisiones, sin embargo las últimas se deben a que la aplicación server recibió el “EOF
notification”, mandó su correspondiente ACK que no llegó y la aplicación se cerró. Entonces
el cliente se queda enviando “EOF notification” pero la otra aplicación ya no está corriendo.
Por eso lo único que se puede asegurar, descartando las últimas 8 retransmisiones, es que
23 − 8
PLRestimado =
0,54 . Lo cual es aproximadamente el parámetro configurado.
36 − 8
Luego se verificó la integridad del archivo y concordaban byte a byte.
Página 12
66.48 Seminario de Redes
MOG
Captura con Bw 100Kbps en UDP
Esta prueba se realizó con un “traffic shaper” para contrastar contra la anterior captura,
exceptuando que no hay perdida de paquetes aleatoria, sólo por overflow de colas(en el “traffic
shaper”) o buffers(en la aplicación).
Los comandos utilizados para simular un medio de transmisión de 100Kbps con una
cola de 5KBytes fueron los siguientes.
ipfw pipe 1 config bw 100Kbit/s queue 5KBytes
ipfw pipe 2 config bw 100Kbit/s queue 5KBytes
ipfw add 101 pipe 1 ip from 192.168.2.84 to 192.168.2.99 in
ipfw add 102 pipe 2 ip from 192.168.2.99 to 192.168.2.84 out
Las primeras dos líneas definen los pipes en ambos sentidos de transmisión y las segundas dos
relacionan los pipes con pares de direcciones IP.
Se ejecutaron las siguientes líneas de comando.
192.168.2.99 – TTY01 #tcpdump -i rl0 -s 0 udp port 1024
192.168.2.99 – TTY02 #./mogsocket udp server 1024 save1
192.168.2.84#./mogsocket udp client 192.168.2.99 1024 arch1 1000
Tramas
No.
Î
Í
Î
Í
Î
Í
Î
Í
Î
Í
Î
Í
Î
Í
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Time
0.000000
0.089223
0.090586
0.167412
0.169108
0.247390
0.249075
0.335263
0.336241
0.427728
0.430191
0.432383
0.433975
0.436186
Source
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
Destination
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
192.168.2.99
192.168.2.84
Protocol
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
UDP
Info
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
Src.port:
32770
1024
32770
1024
32770
1024
32770
1024
32770
1024
32770
1024
32770
1024
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Dst.port: 1024
Dst.port: 32770
Seq#
1000
1000
2000
2000
3000
3000
4000
4000
5000
5000
5020
5020
-1
-1
Type
data
ACK
data
ACK
data
ACK
data
ACK
data
ACK
data
ACK
EOF
EOF
Como se puede ver la transferencia se realizó sin problemas.
Resumiendo en números:
Nodo A
192.168.2.84 port: 32770
Nodo B
192.168.2.99 port: 1024
Total Packets
14
Total Bytes
5776
A-> B Packets
7
A-> B Bytes:
5356
B->A Packets:
7
B->A Bytes
420
De estos números se puede verificar el ancho de banda restringido por el “traffic
shaper” que da:
Página 13
66.48 Seminario de Redes
MOG
BwA −> B
estimado
=
5356 ⋅ 8
98, 7 Kbps
0,433975
Transferencia via Internet
Se procedió luego de haber hecho pruebas sobre la LAN con traffic shaping a realizar
transferencia entre dos computadoras que estaban conectadas a internet. Ambas de la red de
fibertel y una atrás de un NAPT. Nos pareció oportuno no mostrar un dump de alrededor de
300 líneas para corroborar que la transferencia se realizó exitosamente.
La transferencia se hizo probando ambos capas de transporte: TCP y UDP
UDP
Tamaño del archivo: 215.209 Bytes
Velocidad promedio: 55,5Kbps (extraída del Summary de Ethereal)
Tiempo: 34,36 segundos (tiempo hasta la última trama en Ethereal)
TCP
Tamaño del archivo: 215.209 Bytes
Velocidad promedio: 43,9Kbps (extraída del Summary de Ethereal)
Tiempo: 48,02 segundos (tiempo hasta la última trama en Ethereal)
Para verificar la correcta integridad de la información, se mandó el archivo por e-mail y luego
se le realizó el comando diff de Unix.
Problemas surgidos
A la hora de desarrollar el programa nos topamos con distintos problemas como en todo
proyecto aunque éste sea de pequeña dimensión. Sin embargo, el problema más interesante
con que nos encontramos surgió al pasar de un entorno de LAN a uno de Internet.
El problema era que el buffer de entrada al socket se desbordaba ya que el ancho de banda
en Internet era notablemente más chico que la tasa de escritura al socket. Por ende se perdían
los datos. Este fenómeno se evidenciaba al transferir archivos mayores a 4KBytes y su tamaño
al llegar al otro extremo era aleatorio debido a que las condiciones de Internet son muy
variables.
Esto se solucionó incluyendo en el código la sentencia select para esperar que el buffer
se vacíe y no desborde. Luego de realizado lo antedicho, no hubo problemas de índole similar.
Conclusiones
La realización de una aplicación distribuida nos permitió tomar conciencia de la facilidad
que brindan las API’s, en este caso la de los Berkeley Sockets, que se encargan de manera
transparente al programador de realizar tareas complejas como ser la transmisión de datos
garantizada con sus temporizadores y algoritmos de retransmisión y control de congestión, que
además incluye negociar con dispositivos, accesos a recursos compartidos etc.
Por otro lado, cuando se opta por una capa de transporte más simple como ser UDP, ahí
entra en juego la habilidad e inteligencia del programador para poner en marcha un protocolo
Página 14
66.48 Seminario de Redes
MOG
de comunicaciones que realice las tareas pertinentes requeridas por la aplicación sin producir
bloqueos ante cualquier situación inesperada o fuera de estándares. Es decir, que se da más
libertad al programador a costa de una mayor complejidad algorítmica para realizar una tarea
determinada. Este hecho se puede observar en nuestro código mogsocket.c ya que hay mayor
cantidad de líneas para poder implementar un simple ARQ STOP&WAIT para llevar a cabo la
tarea de “file transfer” en comparación con el cliente y servidor TCP.
Página 15
66.48 Seminario de Redes
MOG
Apéndice
Codigo fuente del archivo mogsocket.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
struct
struct
struct
struct
struct
<stdio.h>
<stdlib.h>
<sys/types.h>
<sys/socket.h>
<string.h>
<netdb.h>
<netinet/in.h>
<sys/time.h>
<unistd.h>
NBUFFER 65536
FALSE 0
TRUE 1
h_addr h_addr_list[0]
/*for backward compatibility*/
sockaddr_in sockin;
sockaddr client;
/*MODO SERVER: datos del cliente entrante*/
sockaddr_in serverendpoint;
/*MODO CLIENT: a donde me voy a conectar*/
sockaddr_in fsin;
hostent *hp, *gethostbyname();
/* variables para el select() */
fd_set wfds,rfds;
struct timeval tv;
int retval;
socklen_t client_length;
/*descriptores de sockets*/
int listener, s;
/*variables generales*/
int inbytes=-1;
int outbytes;
int port;
char buffer[NBUFFER];
int chunksize;
FILE *archivo;
long n;
long udp_seq; /*Sequence number del protocolo STOP&WAIT*/
int alen;
char
char
char
char
IS_TCP=FALSE;
IS_UDP=FALSE;
IS_CLIENT=FALSE;
IS_SERVER=FALSE;
int main(int argc, char *argv[])
{
/*Verifica que la cantidad de argumentos este bien*/
if (argc<5)
{
printf("USO: %s [udp|tcp] server port [filename]\n",argv[0]);
printf("o\n");
printf("USO: %s [udp|tcp] client IP port filename chunksize\n",argv[0]);
exit(0);
}
/*Determina si el transporte el tcp o udp*/
if (strcmp(argv[1],"tcp")==0)
IS_TCP=TRUE;
else if (strcmp(argv[1],"udp")==0)
IS_UDP=TRUE;
else
{
printf("Error en el argumento 1. Escriba tcp o udp\n");
exit(1);
}
/*Determina si es cliente o servidor*/
if (strcmp(argv[2],"client")==0)
IS_CLIENT=TRUE;
else if (strcmp(argv[2],"server")==0)
IS_SERVER=TRUE;
else
{
printf("Error en el argumento 2. Escriba client o server\n");
exit(2);
}
if (IS_CLIENT&&(argc!=7))
{
printf("USO: %s [udp|tcp] client IP port filename chunksize\n",argv[0]);
exit(0);
}
/*Convertir el string en un entero*/
if (IS_SERVER)
Página 16
66.48 Seminario de Redes
MOG
port = strtol(argv[3],NULL,10);
if (IS_CLIENT)
{
port = strtol(argv[4],NULL,10);
chunksize = strtol(argv[6],NULL,10);
}
/*Help de linea de Comando*/
if ( (IS_TCP||IS_UDP)&&(IS_SERVER||IS_CLIENT) )
{
printf("MOG socket Software - 2004 - Seminario de Redes\n");
if (IS_CLIENT)
printf("%s de %s a la IP %s en el puerto %d \n",IS_CLIENT?"CLIENTE":"SERVER",
IS_TCP?"TCP":"UDP",argv[3],port);
else
printf("%s de %s en el puerto %d\n",IS_CLIENT?"CLIENTE":"SERVER", IS_TCP?"TCP":"UDP",port);
}
/*Parte del TCP server*/
if ( IS_TCP&&IS_SERVER )
{
listener = socket (PF_INET,SOCK_STREAM,0);
if (listener < 0)
{
printf("No se pudo crear el socket.\n\n");
exit(0);
}
memset(&sockin,0,sizeof(sockin));
sockin.sin_family = AF_INET;
sockin.sin_addr.s_addr = INADDR_ANY;
sockin.sin_port = htons(port);
if (bind(listener,(struct sockaddr *)&sockin,sizeof(sockin)) < 0)
{
printf("Error al hacer Bind\n");
exit(1);
}
if (listen(listener,5) < 0)
{
printf("No puedo escuchar al puerto");
exit(2);
}
s = accept(listener,&client,&client_length);
archivo = fopen(argv[4],"w+"); /*abre archivo para escritura*/
if (archivo==NULL)
{
printf("\nNo se pudo abrir el archivo %s\n",argv[4]);
exit(2);
}
printf("Archivo Abierto exitosamente\n");
n=0;
memset(buffer,0,sizeof(buffer));
while (1)
{
printf("socket descriptor: %d inbytes=%d\t",s,inbytes);
if (inbytes==0)
{
close(s);
exit(1);
}
inbytes = read(s,buffer,sizeof(buffer));
n+=inbytes;
/*write(s,buffer,inbytes);*/
fwrite(buffer,1,inbytes,archivo);
printf("Writen till byte %d\n",n);
}
fclose(archivo);
}/*fin de TCP SERVER*/
/*Parte del TCP client*/
if ( IS_TCP&&IS_CLIENT )
{
s=socket(PF_INET,SOCK_STREAM,0); /* Creo el socket TCP y s es el descriptor */
if (s<1)
printf("No hay socket disponible\n");
else
printf("Se creo el socket\n");
memset(&serverendpoint,0,sizeof(serverendpoint));
serverendpoint.sin_family = AF_INET;
serverendpoint.sin_port = htons(port);
hp = gethostbyname(argv[3]);
bcopy( hp->h_addr,(char *)&serverendpoint.sin_addr, hp->h_length); /* set address */
/* Ahora efectuo la coneccion */
switch (connect(s,(struct sockaddr *)&serverendpoint,sizeof(serverendpoint)))
{
case -1: printf("Error al conectarse\n\n");
Página 17
66.48 Seminario de Redes
case 0:
}
MOG
exit(1);
break;
printf("Esta conectado ( ^C para salir)\n\n");
break;
archivo = fopen(argv[5],"rb"); /*abre archivo para lectura*/
if (archivo==NULL)
{
printf("\nNo se pudo abrir el archivo %s\n",argv[5]);
exit(2);
}
printf("Archivo Abierto exitosamente\n");
do
{
/*variables del select*/
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&wfds);
FD_SET(s, &wfds);
retval = select((s+1), NULL, &wfds, NULL, &tv);
if (FD_ISSET(s, &wfds))
{
printf("Write till byte %d.\n",ftell(archivo));
outbytes = fread(buffer,1,chunksize,archivo);
write(s,buffer,outbytes);
}
else
printf("Unable to send data within 1 second.\n");
}while(!feof(archivo));
}/*fin de TCP CLIENT*/
/*Parte del UDP server*/
if ( IS_UDP&&IS_SERVER )
{
memset(&sockin,0,sizeof(sockin));
sockin.sin_family = AF_INET;
sockin.sin_addr.s_addr = INADDR_ANY;
s = socket (PF_INET,SOCK_DGRAM,0);/* Creo el socket UDP y s es el descriptor*/
if (s <0)
{
printf("Error al crear el socket\n");
exit(1);
}
sockin.sin_port = htons(port);
if ( bind(s, (struct sockaddr *)&sockin, sizeof(sockin)) < 0)
{
printf("No fue posible hacer bind al puerto %d\n", port);
exit(2);
}
archivo = fopen(argv[4],"w+"); /*abre archivo para escritura*/
if (archivo==NULL)
{
printf("\nNo se pudo abrir el archivo %s\n",argv[4]);
exit(3);
}
printf("Archivo Abierto exitosamente\n");
n=0;
while (1)
{
alen = sizeof(sockin);
inbytes= recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&fsin,&alen);
if ( inbytes< 0 )
{
printf("Error al hacer recvfrom");
exit(3);
}
else
{
memcpy(&n, buffer, sizeof(n)); /*copia el seq number recibido*/
if ((n==ftell(archivo))&&(n!=0))
printf("Duplicate Packet\n");
else
fwrite(buffer+sizeof(udp_seq),1,(inbytes-sizeof(udp_seq)),archivo);
printf("Writen till byte %d\n",n);
printf("Bytes recibidos en el Payload UDP: %d\n",inbytes);
/*Acknowledge*/
memcpy(&n, buffer, sizeof(n)); /*copia el seq number recibido*/
if (n==-1)
{
udp_seq = -1; /*le confirma el fin de archivo*/
}
else
Página 18
66.48 Seminario de Redes
MOG
udp_seq = ftell(archivo);
memcpy(buffer,&udp_seq,sizeof(udp_seq));
sendto(s, buffer, sizeof(udp_seq), 0, (struct sockaddr *)&fsin, sizeof(fsin));
if (n==-1)
{
fclose(archivo);
close(s);
exit(1);
}
}
}
}/*fin de UDP SERVER*/
/*Parte del UDP client*/
if ( IS_UDP&&IS_CLIENT )
{
s=socket(PF_INET,SOCK_DGRAM,0); /* Creo el socket UDP y s es el descriptor*/
if (s<0)
printf("Error al crear el socket\n");
else
printf("Se creo el socket\n");
memset(&serverendpoint,0,sizeof(serverendpoint));
serverendpoint.sin_family = AF_INET;
serverendpoint.sin_port = htons(port);
hp = gethostbyname(argv[3]);
bcopy( hp->h_addr,(char *)&serverendpoint.sin_addr, hp->h_length); /* set address */
archivo = fopen(argv[5],"rb"); /*abre archivo para lectura*/
if (archivo==NULL)
{
printf("\nNo se pudo abrir el archivo %s\n",argv[5]);
exit(2);
}
printf("Archivo Abierto exitosamente\n");
/*Armado del Payload de UDP*/
outbytes=0;
outbytes = fread(buffer+(sizeof(udp_seq)),1,chunksize,archivo);
udp_seq = ftell(archivo);
memcpy(buffer,&udp_seq,sizeof(udp_seq));
outbytes +=sizeof(udp_seq);
while(1)
{
if (0==sendto(s,buffer,outbytes,0,(struct sockaddr *)&serverendpoint,sizeof(serverendpoint)))
printf("Error al Escribir \n");
/*variables del select*/
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(s, &rfds);
retval = select((s+1), &rfds, NULL, NULL, &tv);
if (FD_ISSET(s, &rfds))
{
alen = sizeof(sockin);
inbytes = recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&fsin,&alen);
memcpy(&n, buffer, sizeof(n)); /*copia el seq number recibido para verificar si debe retransmitir*/
printf("Remote Sequence number %d
filepos %d\n",n, ftell(archivo));
if (n==-1)
{
fclose(archivo);
close(s);
printf("Archivo transferido exitosamente\n");
exit(1);
}
if (n==ftell(archivo))
{
/*Armado del Payload de UDP*/
outbytes=0;
if (!feof(archivo))
{
outbytes = fread(buffer+(sizeof(udp_seq)),1,chunksize,archivo);
udp_seq = ftell(archivo);
}
else
udp_seq = -1; /*eof notification*/
memcpy(buffer,&udp_seq,sizeof(udp_seq));
outbytes +=sizeof(udp_seq);
}
}
else
{
printf("Acknowledge expected within 2 second.\n");
printf("Remote Sequence number %d
filepos %d\n",n, ftell(archivo));
}
Página 19
66.48 Seminario de Redes
MOG
};
}/*fin de UDP CLIENT*/
exit(0);
}/*fin de main*/
Página 20
Descargar