Implemented the first three phases of neighbour management (warning: for testing purposes, the delays before any action have been reduced)
This commit is contained in:
@ -8,8 +8,9 @@ from threading import Thread, RLock
|
||||
import curses
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
|
||||
from .messages import Packet, DataTLV
|
||||
from .messages import Packet, DataTLV, HelloTLV, GoAwayTLV, GoAwayType
|
||||
|
||||
|
||||
class Hazelnut:
|
||||
@ -72,8 +73,13 @@ class Squirrel(Hazelnut):
|
||||
#dictionnaries of neighbours
|
||||
self.potentialhazelnuts = dict()
|
||||
self.activehazelnuts = dict() #of the form [hazelnut, time of last
|
||||
#hello, time of last long hello, is symetric]
|
||||
#hello, time of last long hello]
|
||||
self.nbNS = 0
|
||||
self.minNS = 3 #minimal number of symetric neighbours a squirrel needs
|
||||
#to have.
|
||||
|
||||
self.add_system_message(f"Listening on {self.address}:{self.port}")
|
||||
self.add_system_message(f"I am {self.id}")
|
||||
|
||||
def new_hazel(self, address: IPv6Address, port: int) -> Hazelnut:
|
||||
"""
|
||||
@ -89,7 +95,7 @@ class Squirrel(Hazelnut):
|
||||
return (hazel.address, hazel.port) in self.potentialhazelnuts
|
||||
|
||||
def remove_from_potential(self, address: IPv6Address, port: int)-> None:
|
||||
self.potentialhazelnuts.pop((address, port), None)
|
||||
self.potentialhazelnuts.pop((str(address), port), None)
|
||||
|
||||
def find_hazelnut(self, address: str, port: int) -> Hazelnut:
|
||||
"""
|
||||
@ -105,16 +111,22 @@ class Squirrel(Hazelnut):
|
||||
"""
|
||||
Send a formatted packet to a client.
|
||||
"""
|
||||
self.refresh_lock.acquire()
|
||||
if len(pkt) > 1024:
|
||||
# The packet is too large to be sent by the protocol. We split the packet in subpackets.
|
||||
return sum(self.send_packet(client, subpkt) for subpkt in pkt.split(1024))
|
||||
return self.send_raw_data(client, pkt.marshal())
|
||||
res = self.send_raw_data(client, pkt.marshal())
|
||||
self.refresh_lock.release()
|
||||
return res
|
||||
|
||||
def send_raw_data(self, client: Hazelnut, data: bytes) -> int:
|
||||
"""
|
||||
Send a raw packet to a client.
|
||||
"""
|
||||
return self.socket.sendto(data, (str(client.address), client.port))
|
||||
self.refresh_lock.acquire()
|
||||
res = self.socket.sendto(data, (str(client.address), client.port))
|
||||
self.refresh_lock.release()
|
||||
return res
|
||||
|
||||
def receive_packet(self) -> Tuple[Packet, Hazelnut]:
|
||||
"""
|
||||
@ -225,7 +237,7 @@ class Squirrel(Hazelnut):
|
||||
|
||||
pkt = Packet.construct(DataTLV.construct(msg, self))
|
||||
for hazelnut in list(self.activehazelnuts.values()):
|
||||
self.send_packet(hazelnut, pkt)
|
||||
self.send_packet(hazelnut[0], pkt)
|
||||
|
||||
def handle_mouse_click(self, y: int, x: int, attr: int) -> None:
|
||||
"""
|
||||
@ -488,8 +500,71 @@ class Squirrel(Hazelnut):
|
||||
curses.LINES - 2, curses.COLS - 2)
|
||||
|
||||
self.refresh_lock.release()
|
||||
|
||||
|
||||
|
||||
def potential_to_contact(self) -> list:
|
||||
"""
|
||||
Returns a list of hazelnuts the squirrel should contact if it does
|
||||
not have enough symmetric neighbours.
|
||||
"""
|
||||
self.refresh_lock.acquire()
|
||||
|
||||
res = []
|
||||
lp = len(self.potentialhazelnuts)
|
||||
val = list(self.potentialhazelnuts.values())
|
||||
|
||||
for i in range(min(lp, max(0,self.minNS-self.nbNS))) :
|
||||
a = randint(0, lp-1)
|
||||
res.append(val[a])
|
||||
|
||||
self.refresh_lock.release()
|
||||
return res
|
||||
|
||||
def send_hello(self) -> None:
|
||||
"""
|
||||
Sends a long HelloTLV to all active neighbours.
|
||||
"""
|
||||
self.refresh_lock.acquire()
|
||||
|
||||
for hazelnut in self.activehazelnuts.values() :
|
||||
htlv = HelloTLV().construct(16, self, hazelnut[0])
|
||||
pkt = Packet().construct(htlv)
|
||||
self.send_packet(hazelnut[0], pkt)
|
||||
|
||||
self.refresh_lock.release()
|
||||
|
||||
def verify_activity(self) -> None:
|
||||
"""
|
||||
All neighbours that have not sent a HelloTLV in the last 2
|
||||
minutes are considered not active.
|
||||
"""
|
||||
self.refresh_lock.acquire()
|
||||
|
||||
val = list(self.activehazelnuts.values()) #create a copy because the dict size will change
|
||||
|
||||
for hazelnut in val :
|
||||
if time.time()-hazelnut[1]>10: #2*60:
|
||||
gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, "you did not talk to me")
|
||||
pkt = Packet().construct(gatlv)
|
||||
self.send_packet(hazelnut[0], pkt)
|
||||
self.activehazelnuts.pop((str(hazelnut[0].address), hazelnut[0].port))
|
||||
self.potentialhazelnuts[(str(hazelnut[0].address), hazelnut[0].port)] = hazelnut[0]
|
||||
|
||||
self.refresh_lock.release()
|
||||
|
||||
def send_neighbours(self) -> None:
|
||||
"""
|
||||
Update the number of symmetric neighbours and
|
||||
send all neighbours NeighbourTLV
|
||||
"""
|
||||
self.refresh_lock.acquire()
|
||||
|
||||
nbNS = 0
|
||||
#for hazelnut in self.activehazelnuts.values() :
|
||||
# if time.time()-hazelnut[2]<2*60: #could send the same to all neighbour, but it means that neighbour A could receive a message with itself in it -> if the others do not pay attention, trouble
|
||||
|
||||
|
||||
self.refresh_lock.release()
|
||||
|
||||
class Worm(Thread):
|
||||
"""
|
||||
The worm is the hazel listener.
|
||||
@ -513,6 +588,43 @@ class Worm(Thread):
|
||||
self.squirrel.refresh_history()
|
||||
self.squirrel.refresh_input()
|
||||
|
||||
class HazelManager(Thread):
|
||||
"""
|
||||
A process to cleanly manage the squirrel's neighbours
|
||||
"""
|
||||
def __init__(self, squirrel: Squirrel, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.squirrel = squirrel
|
||||
self.last_potential = 0
|
||||
self.last_check = 0
|
||||
self.last_neighbour = 0
|
||||
|
||||
htlv = HelloTLV().construct(8, self.squirrel)
|
||||
pkt = Packet().construct(htlv)
|
||||
self.hellopkt = pkt
|
||||
|
||||
def run(self) -> None:
|
||||
while True:
|
||||
#First part of neighbour management: ensure the squirrel has enough
|
||||
#symmetric neighbours.
|
||||
if time.time()-self.last_potential > 5:
|
||||
to_contact = self.squirrel.potential_to_contact()
|
||||
|
||||
for hazel in to_contact :
|
||||
self.squirrel.send_packet(hazel, self.hellopkt)
|
||||
self.last_potential = time.time()
|
||||
|
||||
#Second part: send long HelloTLVs to neighbours every 30 seconds
|
||||
if time.time()-self.last_check > 5: #30 :
|
||||
self.squirrel.send_hello()
|
||||
self.last_check = time.time()
|
||||
|
||||
#Third part: get rid of inactive neighbours
|
||||
self.squirrel.verify_activity()
|
||||
|
||||
#Fourth part: verify symmetric neighbours and send NeighbourTLV every minute
|
||||
|
||||
|
||||
|
||||
class Message:
|
||||
"""
|
||||
|
Reference in New Issue
Block a user