class Host: def __init__(self, ip, port, server_ip): self.ip = ip #Hosts ip self.port = port # Host port self.serverPC_ip = server_ip #ServerPC ip the host is connecting to self.x_scalar = 50 #for incrementing the x_min to an x_max value self.curr_x_max = 0 self.curr_x_min = 50 self.curr_x_min_ip = '' self.left_neighbor = '' self.right_neighbor = '' self.l_neighbor = '' #Area of the shared area between the host and the left neighbor self.r_neighbor = '' #Area shared between the host and the right neighbor self.host_ips = [] #The list of all connected hosts on the network self.connections = [] #A list of connection objects self.work_queue = [ ] #The queue of instructions for the host to execute self.all_alphas = [] #List of all host alphas self.running = True #Running and updated are semaphores responsible for flagging when the program should execute self.updated = False self.updates_received = [ ] # this will be for keeping track of what host we've received an HUPD from self.run() def parseMessage(self, sock): '''ParseMessage is responsible for recieving messages from sockets and and returning them as a string''' try: msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: raise ConnectionError('Socket is closed - 1') if byte == b'\n': break msg += byte datatype = msg.decode() msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: raise ConnectionError('Socket is closed - 2') if byte == b'\n': break msg += byte origin = msg.decode() msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: raise ConnectionError('Socket is closed - 3') if byte == b'\n': break msg += byte payload = msg.decode() return Message(datatype, origin, payload) except ConnectionError as err: print("Error: {0} ".format(err)) return None def parseMessageHost(self, conn): '''ParseMessageHost is responsible for recieving messages from host-related sockets and and returning them as a Message object''' msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: print('Host ' + conn.ip + ' was lost') range = conn.max_x - conn.min_x if self.l_neighbor == conn.ip: self.x_min -= int((range / 2.0) + .5) self.host_info.x_min -= int((self.x_scalar / 2.0) + .5) self.host_info.merge_left_backups() if self.r_neighbor == conn.ip: self.x_max += int((range / 2.0) + .5) self.host_info.x_max += int((self.x_scalar / 2.0) + .5) self.host_info.merge_right_backups() conn.close() if len(self.connections) == 1: self.host_info.alone = True self.host_ips.remove(conn.ip) self.connections.remove(conn) return if byte == b'\n': break msg += byte datatype = msg.decode() msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: print('Host ' + conn.ip + ' was lost') range = conn.max_x - conn.min_x if self.l_neighbor == conn.ip: self.x_min -= int((range / 2.0) + .5) self.host_info.x_min -= int((self.x_scalar / 2.0) + .5) self.host_info.merge_left_backups() if self.r_neighbor == conn.ip: self.x_max += int((range / 2.0) + .5) self.host_info.x_max += int((self.x_scalar / 2.0) + .5) self.host_info.merge_right_backup() conn.close() if len(self.connections) == 1: self.host_info.alone = True self.host_ips.remove(conn.ip) self.connections.remove(conn) return if byte == b'\n': break msg += byte origin = msg.decode() msg = b'' while True: byte = sock.recv(1) if len(byte) == 0: print('Host ' + conn.ip + ' was lost') range = conn.max_x - conn.min_x if self.l_neighbor == conn.ip: self.x_min -= int((range / 2.0) + .5) self.host_info.x_min -= int((self.x_scalar / 2.0) + .5) self.host_info.merge_left_backups() if self.r_neighbor == conn.ip: self.x_max += int((range / 2.0) + .5) self.host_info.x_max += int((self.x_scalar / 2.0) + .5) self.host_info.merge_right_backup() conn.close() if len(self.connections) == 1: self.host_info.alone = True self.host_ips.remove(conn.ip) self.connections.remove(conn) return if byte == b'\n': break msg += byte payload = msg.decode() return Message(datatype, origin, payload) def connectToServer(self): """ This function initiaties the connection request and sends it to the serverPC to be verified as a host. The function should then recieve an Okay message, and connect to all hosts sent in the message payload. Once the host is connected it will begin the thread listening to the server and work threads """ client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_sock.bind((self.ip, self.port)) client_sock.connect((self.serverPC_ip, 9090)) #create CREQ message message = Message('CREQ', self.ip, '\0') client_sock.sendall(message.generateByteMessage()) print('Sent CREQ message to serverPC at ' + self.serverPC_ip) #recieve response as string response_message = self.parseMessage(client_sock) if (response_message.type == 'OKAY'): print('OKAY message received') #payload contains host area, and list of connected hosts payload_array = response_message.payload.split(',') i = 1 lost_payload = '' while i < len(payload_array): indicator = self.setupHostConnection(payload_array[i]) if (indicator == False): lost_payload += payload_array[i] + "," else: self.host_ips.append(payload_array[i]) i += 1 self.x_min = self.curr_x_max self.x_max = self.x_min + self.x_scalar self.host_info = HostInfo(self.x_min, self.x_max) #this message contains the hosts that have disconnected from the network and notifes the serverPC about the change del_message = Message('LHST', self.ip, lost_payload) print('sent LHST to serverPC with payload: ' + lost_payload) client_sock.sendall(del_message.generateByteMessage()) if self.curr_x_min_ip == '': #there was no other host, you are your own neighbor self.r_neighbor = self.ip self.host_info.r_neighbor_ip = self.ip self.l_neighbor = self.ip self.host_info.l_neighbor_ip = self.ip else: #else, there is another host - check if there are any other hosts self.r_neighbor = self.curr_x_min_ip self.host_info.r_neighbor_ip = self.curr_x_min_ip if self.l_neighbor == '': #there isnt a left neighbor - left neighbor is right neighbor self.l_neighbor = self.r_neighbor self.host_info.l_neighbor_ip = self.r_neighbor else: #there is a left neighbor - set host info left neighbor self.host_info.l_neighbor_ip = self.l_neighbor # need to start the listening thread and the work thread now host_start_thread = Thread(target=lambda: self.startHostInfo()) host_start_thread.start() listening_thread = Thread(target=lambda: self.listeningPort()) listening_thread.daemon = True listening_thread.start() work_thread = Thread(target=lambda: self.processWork()) work_thread.daemon = True work_thread.start() else: print('Invalid message type received from ' + message.origin) def startHostInfo(self): self.host_info.run() def setupHostConnection(self, host_ip): """ This function is responsible for connecting to all hosts on the network. The host will also setup it's left and right neighbor and create the listening threads for host connections """ if host_ip != self.ip and host_ip != '': host_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) indicator = host_socket.connect_ex((host_ip, 9090)) if indicator != 0: return False else: new_host_msg = Message("NHST", self.ip, '\0') host_socket.sendall(new_host_msg.generateByteMessage()) print('NHST message sent to Host at ' + host_ip) area_message = self.parseMessage(host_socket) if (area_message.type == 'AREA'): print('AREA message received from ' + area_message.origin) payload_array = area_message.payload.split(':') curr_host_ip = area_message.origin host_min_x = payload_array[0] host_max_x = payload_array[1] if host_max_x > self.curr_x_max: self.curr_x_max = host_max_x if self.min_x == host_max_x: self.l_neighbor = curr_host_ip if host_min_x <= self.curr_x_min: self.curr_x_min = host_min_x self.curr_x_min_ip = curr_host_ip new_thread = Thread( target=lambda: self.listenToHost(host_socket)) new_thread.daemon = True new_thread.start() new_connection = Connection(host_ip, host_socket, new_thread, host_min_x, host_max_x) self.connections.append(new_connection) return True else: print('Invalid message type received from ' + area_message.origin + ' - Host corrupt') return False return True def listeningPort(self): """ The listening port uses a binded socket to accept new incoming host connections. Once accepted the function will add the NHST instruction to the work queue """ listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listening_socket.bind((self.ip, 9090)) listening_socket.listen(1) while self.running == True: new_conn_sock, (new_conn_ip, new_conn_port) = listening_socket.accept() message = self.parseMessage(new_conn_sock) if (message.type == 'NHST'): print('Got NHST message from ' + message.origin) new_instruction = Instruction('NHST') new_instruction.message = message new_instruction.sock = new_conn_sock self.work_queue.append(new_instruction) else: print('Invalid Message Type received from ' + message.origin) new_conn_sock.close() return def processWork(self): """ processWork goes through the intruction objects in the work_queue processWork prioritizes certain instructions in order to implement implicit """ while self.running == True: if len(self.work_queue) == 0: self.work_queue = [ Instruction('Do Math'), Instruction('Send HUPD'), Instruction('Receive All HUPDs') ] else: instruction = self.work_queue.pop(0) print('Instruction is : ' + instruction.type) if instruction.type == 'Do Math': #start calculations self.updated = False #print('Doing Math') # run calculations elif instruction.type == 'Send HUPD': #echo host update to all other hosts on the network all_l, all_r = self.host_info.get_our_backup() all_l_alphas, all_r_alphas = self.host_info.get_our_alpha_backup( ) min_max = str(self.x_min) + ':' + str(self.x_max) self.host_info.create_left_halo() self.host_info.create_right_halo() payload = self.host_info.numpy_array_to_string( self.host_info.my_aboids ) + '\0' + self.host_info.numpy_array_to_string( self.host_info.l_halo ) + '\0' + self.host_info.numpy_array_to_string( self.host_info.r_halo ) + '\0' + all_l + '\0' + all_r + '\0' + all_l_alphas + '\0' + all_r_alphas + '\0' + min_max + '\0' our_update = Message("HUPD", self.ip, payload) q = input('got to Send HUPD 1') #if there are no connections, send to myself if len(self.connections) == 0: self.updateSelf(our_update) else: for connection in self.connections: connection.sock.sendall( our_update.generateByteMessage()) self.host_info.update_my_aboids() print('Sent Out HUPD') elif instruction.type == 'Receive All HUPDs': # make sure to receive all HUPDs from listening threads if len(self.connections) > 0: while len(self.updates_received) != len( self.connections): msg = 'wait' # only set to true once all updates have been received self.updated = True self.updates_received = [] # Once all updates are recieved update ABoid locations self.host_info.merge_halos() self.host_info.update_all_aboids(self.all_alphas) self.all_alphas = [] elif instruction.type == 'NHST': #New host tring to connect to network new_host_ip = instruction.message.origin payload_array = instruction.message.payload.split(':') new_host_min_x = payload_array[0] new_host_max_x = payload_array[1] #check if the new host is a neighbor if self.x_max == new_host_min_x: self.r_neighbor = new_host_ip self.host_info.r_neighbor_ip = new_host_ip if self.x_min == 0: self.l_neighbor = new_host_ip self.host_info.l_neighbor_ip = new_host_ip self.host_ips.append(new_host_ip) #Start the thread that is listening to the socket connected to the new host new_thread = Thread( target=lambda: self.listenToHost(instruction.sock)) new_thread.daemon = True new_thread.start() new_connection = Connection(new_host_ip, instruction.sock, new_thread, new_host_min_x, new_host_max_x) self.connections.append(new_connection) host_area = self.x_min + ':' + self.x_max #send current host area to the newly connected host area_message = Message('AREA', self.ip, host_area) instruction.sock.sendall( area_message.generateByteMessage()) print('Sent AREA message to ' + new_host_ip) elif instruction.type == 'LHST': #Host has disconnected to the network for host_ip in self.host_ips: if host_ip == instruction.message.origin: #remove host from list of connected ips self.host_ips.remove(host_ip) for connection in self.connections: #remove the connection object from list of known connections if connection.ip == instruction.message.origin: #close the hosts socket and thread if len(self.connections) == 1: self.host_info.alone = True connection.close() self.connections.remove(connection) else: print('Invalid Instruction - skipping...') return def updateSelf(self, message): q = input("press enter") self.updated = False self.host_info.alone = True host_ip = message.origin payload = message.payload.split('\0') host_alphas = payload[0] print('host_alphas = ' + host_alphas) num_alphas = len(host_alphas.split(',')) for i in range(num_alphas): self.all_alphas.append(host_alphas.split(',')[i - 1]) host_l_halo = payload[1] host_r_halo = payload[2] host_all_l = payload[3] host_all_r = payload[4] l_alpha_backup = payload[5] r_alpha_backup = payload[6] host_min = payload[7].split(':')[0] host_max = payload[7].split(':')[1] #if the hosts left neighbor then store the halo region data and create back ups of left neighbor data self.host_info.n_l_halo = self.host_info.string_to_numpy_array( host_r_halo) self.host_info.l_backup = host_all_r self.host_info.l_backup_alphas = r_alpha_backup #if the hosts right neighbor then store the halo region data and create back ups of right neighbor data self.host_info.n_r_halo = self.host_info.string_to_numpy_array( host_l_halo) self.host_info.r_backup = host_all_l self.host_info.r_backup_alphas = l_alpha_backup self.updated = True def listenToHost(self, host_sock): """ listenToHost recieves messages from other hosts If a host update is received the host will check if it is a neighbor, and then update the neighbor halo region and backups If a lost host message is recieved it will create the instruction type LHST and added to the queue """ while self.running == True: #turn message into string host_connection = None for conn in self.connections: if conn.sock == host_sock: host_connection = conn message = self.parseMessageHost(host_connection) if message.type == 'HUPD': #host update message payload print('Got HUPD from ' + message.origin) self.updated = False host_ip = message.origin payload = message.payload.split('\0') host_alphas = payload[0] num_alphas = count(host_alphas.split(',')) for i in range(num_alphas): self.all_alphas.append(host_alphas.split(',')[i - 1]) host_l_halo = payload[1] host_r_halo = payload[2] host_all_l = payload[3] host_all_r = payload[4] l_alpha_backup = payload[5] r_alpha_backup = payload[6] host_min = payload[7].split(':')[0] host_max = payload[7].split(':')[1] if self.l_neighbor == host_ip: #if the hosts left neighbor then store the halo region data and create back ups of left neighbor data self.host_info.n_l_halo = self.host_info.string_to_numpy_array( host_r_halo) self.host_info.l_backup = host_all_r self.host_info.l_backup_alphas = r_alpha_backup if self.r_neighbor == host_ip: #if the hosts right neighbor then store the halo region data and create back ups of right neighbor data self.host_info.n_r_halo = self.host_info.string_to_numpy_array( host_l_halo) self.host_info.r_backup = host_all_l self.host_info.r_backup_alphas = l_alpha_backup #may need to parse the different Alpha coordinates before appending to all_alphas self.all_alphas.append(host_alphas) self.updates_received.append(host_ip) while (self.updated != True): wait = 'wait' elif message.type == 'LHST': #LHST message recieved meaning that the host must close that connected socket print('Got LHST from ' + message.origin) new_instruction = Instruction('LHST') new_instruction.message = message self.work_queue.append(new_instruction) else: print('Invalid message type received from ' + message.origin) return def run(self): main_thread = Thread(target=lambda: self.connectToServer()) main_thread.daemon = True main_thread.start() while (self.running == True): user_input = input('Enter "quit" to end program: ') if user_input == 'quit': print('Quitting...') self.running = False