Peers can start discovery at ~ the same time

Still needs some improvement, hard to test because socket has to be bound.
This commit is contained in:
Philip Trauner 2016-10-09 00:52:34 +02:00
parent 1ee7b787ed
commit 55f9dd4425

View file

@ -1,13 +1,15 @@
from socket import * from socket import *
from random import getrandbits from random import getrandbits
from random import choice
from time import time from time import time
from time import sleep from time import sleep
import Utils import Utils
import Logging import Logging
from _thread import start_new_thread from _thread import start_new_thread
HEY = "HEY".encode() HEY = "HEY".encode()
IDENT_SEED_LENGTH = 64 NAY = "NAY".encode()
WHAT = "WHAT".encode() WHAT = "WHAT".encode()
@ -17,80 +19,79 @@ class MissingInterfaceError(TypeError):
class Disc0very: class Disc0very:
def __init__(self, port, interface="eth0", max_peers=32): def __init__(self, port, interface=None, max_peers=32):
self.port = port self.port = port
self.ident = getrandbits(max_peers * 2)
self.sock = socket(AF_INET, SOCK_DGRAM) self.sock = socket(AF_INET, SOCK_DGRAM)
self.sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) self.sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
self.sock.bind(("", self.port)) self.sock.bind(("", self.port))
self.ip_address = Utils.get_ip_address(interface)
self.discovering = False self.discovering = False
self.enlisting = False self.enlisting = False
# Not thread-safe if ran in parallel with __enlist
def discover(self, time_out=2): def discover(self, time_out=1):
if not self.enlisting: self.sock.setblocking(False)
self.discovering = True end_time = time() + time_out
self.sock.setblocking(False) servers = []
self.sock.sendto(HEY, ('255.255.255.255', self.port)) self.sock.sendto(HEY, ('255.255.255.255', self.port))
end_time = time() + time_out while time() < end_time:
servers = [] try:
while time() < end_time: data, address = self.sock.recvfrom(512)
try: address = address[0]
data = self.sock.recv(512) # If enlisted peer responds with ip address
if data != HEY: if data not in (HEY, NAY) and address != self.ip_address and not data in servers:
servers.append(data) servers.append(data)
except BlockingIOError: # If another peer is currently discovering
sleep(0.2) elif data == HEY and address != self.ip_address:
self.discovering = False self.sock.sendto(NAY, ('255.255.255.255', self.port))
if len(servers) == 0: return self.discover(time_out=time_out)
return None # If another peer gave up
elif len(servers) > 1: elif data == NAY and address != self.ip_address:
self.sock.sendto(WHAT, ('255.255.255.255', self.port)) return None
else: except BlockingIOError:
return servers[0] sleep((choice(range(1, 10)) / 2) / 10)
if len(servers) == 0:
return None
elif len(servers) > 1:
self.sock.sendto(WHAT, ('255.255.255.255', self.port))
else:
return servers[0]
def enlist(self, interface, blocking=False): def enlist(self, interface, blocking=False):
if not self.discovering: if blocking:
if blocking: self.__enlist(interface)
self.__enlist(interface) else:
else: start_new_thread(self.__enlist, (interface, ))
start_new_thread(self.__enlist, (interface, ))
# Not thread-safe if ran in parallel with discover
# Interface should always be provided when using a Wallaby # Interface should always be provided when using a Wallaby
# because wlan0 and wlan1 have an IP address assigned # because wlan0 and wlan1 have an IP address assigned
def __enlist(self, interface=None): def __enlist(self, interface=None):
self.enlisting = True
self.sock.setblocking(True) self.sock.setblocking(True)
ip_address = Utils.get_ip_address(interface)
data = "" data = ""
while True: while True:
try: try:
data = self.sock.recv(512) data, address = self.sock.recvfrom(512)
except BlockingIOError: except BlockingIOError:
sleep(0.1) sleep(0.1)
if data == HEY: if data == HEY:
self.sock.sendto(ip_address.encode(), ('255.255.255.255', self.port)) self.sock.sendto(self.ip_address.encode(), ('255.255.255.255', self.port))
elif data == WHAT: elif data == WHAT:
Logging.error("Apparently more than one server is running. " Logging.error("Apparently more than one server is running. "
"Investigating...") "Investigating...")
# Discover and if other server is found shutdown # Discover and if other server is found shutdown
def start():
print("starting")
if __name__ == "__main__": if __name__ == "__main__":
from time import sleep
disc0very = Disc0very(3077) disc0very = Disc0very(3077)
server = disc0very.discover() server = disc0very.discover()
if not server: if not server:
print("enlisted") print("enlisting")
disc0very.enlist(None, blocking=True) disc0very.enlist(None, blocking=True)
else: else:
print(server) print(server)