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:
eichhornchen
2020-12-27 21:31:34 +01:00
parent 806287f834
commit b7a495eb11
3 changed files with 150 additions and 28 deletions

View File

@ -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:
"""