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,34 +19,40 @@ 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.discovering = True
self.sock.setblocking(False) self.sock.setblocking(False)
self.sock.sendto(HEY, ('255.255.255.255', self.port))
end_time = time() + time_out end_time = time() + time_out
servers = [] servers = []
self.sock.sendto(HEY, ('255.255.255.255', self.port))
while time() < end_time: while time() < end_time:
try: try:
data = self.sock.recv(512) data, address = self.sock.recvfrom(512)
if data != HEY: address = address[0]
# If enlisted peer responds with ip address
if data not in (HEY, NAY) and address != self.ip_address and not data in servers:
servers.append(data) servers.append(data)
# If another peer is currently discovering
elif data == HEY and address != self.ip_address:
self.sock.sendto(NAY, ('255.255.255.255', self.port))
return self.discover(time_out=time_out)
# If another peer gave up
elif data == NAY and address != self.ip_address:
return None
except BlockingIOError: except BlockingIOError:
sleep(0.2) sleep((choice(range(1, 10)) / 2) / 10)
self.discovering = False
if len(servers) == 0: if len(servers) == 0:
return None return None
elif len(servers) > 1: elif len(servers) > 1:
@ -54,43 +62,36 @@ class Disc0very:
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)