20 gennaio, 2007

Esperimento: Una rete che si autoespande

In questo post voglio parlarvi di un esperimento che ho fatto in questi giorni con python. In pratica si tratta di una rete di nodi che si "auto trovano" nella rete mantenendo ognuno una lista con gli ip e i nomi degli altri nodi che viene aggiornata non appena un nodo viene a mancare. La cosa interessante è che non c'è un server centrale a gestire il tutto, ma ogni nodo è uguale all'altro ed ha la stessa importanza.

Come Funziona?
Ogni nodo (che in pratica è un programma python) è composto da 4 trhead ed un loop principale. Un thread (Broadcaster) si occupa di fare il broadcast sulla rete (N.B. quindi funziona solo sulla rete locale) della sua lista di contatti, che contiene l'ip e il nome degli altri nodi. Un secondo thread (Receiver) si occupa di ricevere il traffico broadcast inviato dagli altri nodi e di aggiornare la lista dei contatti. Un terzo thread (TcpListner) rimane in ascolto per connessioni tcp (questo serve agli altri nodi per capire se il nodo è ancora "vivo"). Infine un quarto thread (BlistCleaner) che si occupa di interrogare i nodi nella sua lista per scoprire se sono ancora attivi o meno e di aggiornare di conseguenza la sua lista di contatti.
La prima cosa che un nodo fa è quella di mandare in broadcast il suo ip e il suo nome (passati come parametri). Se ad esempio i nodi sono due, il secondo nodo cattura il broadcast del primo e lo inserisce nella sua lista di broadcast che contiene già se stesso e così via con gli altri nodi. Riporto di seguito il codice dei nodi:

PORT = 50000
TCPPORT = 80000
BCASTIP = '192.168.50.255'

import sys, time
import threading
import socket

class Broadcaster(threading.Thread):
def __init__(self, data):
threading.Thread.__init__(self)
self.data = data
self.exit = False

def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while 1:
if self.exit: break
data = ""
print "Broadcasting: ", self.data.broad
for item in self.data.broad:
name, ip = item
data += name + ":" + ip + ","
if data != "":
# tolgo l'ultima virgola
data = data[:len(data) - 1]
try:
s.sendto(data, (BCASTIP, PORT))
except socket.error, e:
break
print "Broadcaster error: ", e
time.sleep(2)
s.close()
print "Exit Broadcaster"

class Receiver(threading.Thread):
def __init__(self, data):
threading.Thread.__init__(self)
self.exit = False
self.data = data

def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', PORT))
s.settimeout(0.1)
while 1:
if self.exit: break
try:
data, fromaddr = s.recvfrom(1024)
items = data.split(",")
self.data.rec = []
if len(items) > 0:
for item in items:
name, ip = item.split(":")
self.data.rec.append( (name, ip) )

print "Received: ", self.data.rec
except socket.timeout, e:
pass
except socket.error, e:
break
print "Receiver error: ", e


#print (data, fromaddr)
#print self.list

s.close()
print "Exit Receiver"

class TcpListner(threading.Thread):

def __init__(self, ip):
threading.Thread.__init__(self)
self.myip = ip
self.exit = False

def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind( (self.myip, TCPPORT) )
s.listen(1)
s.settimeout(0.1)

while 1:
if self.exit: break
try:
conn, addr = s.accept()
except socket.timeout, e:
pass
except socket.error, e:
s.close()
break
print "TcpListner error: ", e

s.close()
print "Exit TcpListner"

class BlistCleaner(threading.Thread):

def __init__(self, data, myname):
threading.Thread.__init__(self)
self.data = data
self.exit = False
self.myname = myname

def run(self):

while 1:
if len(self.data.broad) == 0: continue
for item in self.data.broad:
name, ip = item
if name != self.myname:
#print "check name: ", name, "ip: ", ip
try:
s = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
s.connect((ip, TCPPORT))
s.close()
except socket.error, e:
s.close()
print "Elimino ",name
self.data.broad.remove( (name, ip) )
self.data.rec.remove( (name, ip) )
print "Lista: ", self.data.broad
time.sleep(1)
if self.exit: break

print "Exit BlistCleaner"

class Data:
pass

data = Data()
data.rec = []
data.broad = []


broad = Broadcaster(data)
broad.setName("Broadcaster")
rec = Receiver(data)
rec.setName("Receiver")
# aggiungo il mio nome
name = sys.argv[1]
myip = sys.argv[2]

data.broad.append( (name, myip) )

tcpl = TcpListner(myip)
tcpl.setName("TcpListner")
cleaner = BlistCleaner(data, name)
cleaner.setName("BlistCleaner")

tcpl.start()
cleaner.start()

broad.start()
rec.start()

t = time.time()
while 1:
try:
if len(data.rec) > 0:
for n in data.rec:
if n not in data.broad:
data.broad.append(n)
time.sleep(0.1)
now = time.time()
if now - t > 3:
#print "rlist: ", rec.list
#print "blist: ", broad.list
t = now
except KeyboardInterrupt:
broad.exit = True
rec.exit = True
tcpl.exit = True
cleaner.exit = True
sys.exit(0)

Naturalmente sono da impostare l'indirizzo di broadcast e se salviamo il file come node.py dobbiamo eseguirlo come:

python [nomenodo] [mio_ip]
Per ora è solo un esperimento ma magari in futuro potrebbe risultarmi utile per qualcosa o magari può servire a voi come spunto per una nuova idea!

Nessun commento:

Posta un commento