Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Infraestructura de Redes
5o Ingenierı́a Informática 08/09
import socket
AF INET especifica la familia de procolos que usaremos. Las otras dos posibilidades son
AF UNIX y AF INET6, pero quedan fuera del alcance de esta práctica.
SOCK STREAM define nuestro socket como TCP. Para definir un socket como UDP usarı́amos
la constante SOCK DGRAM. Existen más tipos de sockets, como por ejemplos sockets RAW, que
no usaremos en esta práctica.
Una vez creado el socket, podemos realizar envı́o y recepciones mediante las funciones send
y recv, en el caso de haber definido un socket TCP, o mediante sendto y recvfrom para UDP. Para
finaliar cerraremos el descriptor del socket mediante el método close.
En este punto conviene leer el manual de Python del paquete sockets1 . En él se describe cómo
usar métodos importantes como inicializar la conexión mediante el método connect (para TCP) o
el método bind para escuchar peticiones en un puerto.
Construye un cliente que envı́e peticiones UDP a un host. La dirección IP y el puerto destino
serán configurados mediante argumentos en lı́nea de comandos. De esta forma podrı́amos lanzar
nuestro cliente:
1
Donde 127.0.0.1 es la IP destino (localhost en este caso) y 4000 el puerto.
El programa nos preguntará qué mensaje (string) queremos enviar. El usuario escribirá una
frase que será enviada al servidor. El programa finalizará sólo cuando el usuario escriba quit.
Para parsear los argumentos de la lı́nea de comandos utilizamos el paquete sys de Python.
Nuestro programa recibirá los argumentos en forma de lista, de forma que podemos acceder a
cada elemento de la siguiente forma:
Para probar nuestro cliente necesitamos un proceso escuchando peticiones en algún puerto
determinado. Como todavı́a no hemos construido ningún servidor, usaremos la herramienta netcat.
Para ello, desde consola ejecutamos:
nc -u -l -p 4000
Por último ejecuta el analizador de redes wireshack y estudia los paquetes que están intercam-
biando cliente y servidor. Ya que wireshack necesita permisos de root, utilizaremos la herramienta
sudo para lanzarlo:
Además debemos usar el método bind para escuchar peticiones en el puerto indicado.
El servidor implementará un servicio de echo, es decir, devolverá al cliente el mismo string
que éste le envı́e pero convertido a mayúsculas. Por ejemplo:
2
# lanzamos primero el servidor en un terminal
anavarro@host:˜/$ python servidor UDP.py 4000
Esperando peticiones...
mensaje aceptado!
# lamzamos el cliente
anavarro@host:˜/$ python clienteUDP.py localhost 4000
Escriba mensaje: luke, soy tu padre
El servidor dice: LUKE, SOY TU PADRE
Por último vuelve a lanzar tu práctica con Wireshack para estudiar qué está pasando.
El cliente enviará una petición de tiempo al servidor tomando el timestamp del momento
del envı́o.
El cliente recibe el la respuesta del servidor y vuelve a tomar un timestamp tras la recepción.
import time
t = time.time()
Podéis también echarle un vistazo al paquete datetime2 . Este paquete permite manejar fechas
y horas de forma muy cómoda permitiendo el formato y el acceso a cada uno de los elementos del
timestamp (dı́a, mes, año, hora, minuto...)
2
http://www.python.org/doc/2.5.2/lib/module-datetime.html
3
4. Fase 4: Intercambio RTTs bidireccional
En esta última fase construiremos 2 nodos que envı́en y reciban tiempos. Para facilitar la
construcción de nodos más complejos, nos serviremos del paquete SocketServer3 de Python:
import SocketServer
import SocketServer
import threading
# heredamos de BaseRequestHandler
class MyUDPHandler(SocketServer.ThreadingMixIn, SocketServer.BaseRequestHandler):
# constructor
def __init__ (self, request, client_address, server):
SocketServer.BaseRequestHandler.__init__(self, request, client_address, server)
# metodo handler
def handle (self):
data = self.request[0].strip()
socket = self.request[1]
print "%s wrote:" % self.client_address[0]
print data
socket.sendto (data.upper(), self.client_address)
if __name__ == "__main__":
cnn = ("127.0.0.1", 4000)
server = SocketServer.UDPServer (cnn, MyUDPHandler)
server_thread = threading.Thread (target=server.serve_forever)
server_thread.setDaemon (True)
server_thread.start()
El objetivo de esta fase es crear dos nodos que calculen sus RTTs e intercambien dicho valor
entre ellos. De tal forma que ambos nodos mantendrán dos valores: por un lado el RTT que él
mismo ha calculado y el RTT que el otro nodo ha calculado. Suponiendo que n1 y n2 sean dos
nodos de nuestra red, tendremos dos operaciones que implementar:
4
Obtención de RTT: n1 pide a n2 el tiempo que n2 ha calculado anteriormente. En el que
supuesto que n2 no haya calculado el RTT todavı́a, puede enviar 0.
Si queremos evitar los dos últimos argumentos, podemos hacer que nuestro nodo nos pregunte
en tiempo de ejecución la IP y el puerto del nodo sobre el que queremos calcular y obtener el RTT.
Aunque la solución se puede abordar de muchas formas, recomendamos implementar un sis-
tema básico de mensajes. Será el método handler de la clase SimpleRequestHandler el que deberı́a
implementar la lógica de la aplicación. El siguiente fragmento de código ilustra un ejemplo sobre
cómo discriminar el tipo de mensaje entrante:
msg = self.request[0].strip()
if msg == "GET_TIME":
self.request.send(time)
elif msg == "CALCULATE_TIME":
self.request.send("ACK")
else:
self.request.send("Unknown command")
A la hora de enviar y recibir datos que no sean strings, necesitaremos hacer una serialización
del envı́o. Para ello usaremos el paquete struct de Python y los métodos pack/unpack. Aquı́ se
muestra un ejemplo muy sencillo:
import struct
El primer argumento del método pack especifica el tipo de dato sobre el que se realizará la
conversión. En este ejemplo indicamos que es un tipo float (d) y que además irán por la red (!),
evitando ası́ problemas con el ’endian’. Tenéis más información del paquete struct en la página
del manual4
Para probar la práctica recomendamos lanzar cada nodo en máquinas diferentes y, a ser posi-
ble, desde campus diferentes.
4
http://www.python.org/doc/2.5.2/lib/module-struct.html