def __request_close(self): active_connections = self.__connections.to_list() try: while len(active_connections) != 0: _, writable, _ = select.select([], active_connections, [], 1) for fd in writable: active_connections.remove(fd) BufferWriter.request_close(fd) fd.close() except ConnectionResetError: pass
def occupy(self, id: int, uuid: str): """ Occupy a seat for future connections :param id: str :param uuid: str :return: None """ _tmp_cons = self.__worker_id_to_cons.get(id, None) # if not settled if _tmp_cons is None: self.__worker_id_to_cons[id] = uuid # check which one is right elif isinstance(_tmp_cons, list): for _uuid, _con in _tmp_cons: if _uuid == uuid: self.put(id, _con) # close it else: BufferWriter.request_close(_con) _con.close()
def register(self, id_self, content_package: NodeAssignment, con_from=None): """ Register all workers :param id_self: id of current worker :param content_package: content package that contains address and uuid of all workers :return: None """ self.__id = id_self if con_from is not None: self.__workers.put(Initialization_Server, con_from) self_uuid = None uuid = content_package.uuid writer = BufferWriter() # for all slaves for id, ip_addr in content_package: # slaves who's id before self if self_uuid is None and id != self.__id: self.__workers.occupy(id, uuid) # id of myself elif id == self.__id: self_uuid = uuid # id behind myself elif self_uuid is not None: # try reach worker_con = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: worker_con.connect((ip_addr, STAR_NET_WORKING_PORTS)) # try register data = { Key.Type: Type_Val.WorkerReports, Key.From: self.__id, Key.To: id, Key.Content: self_uuid } writer.set_content(data) writer.send(worker_con) self.__workers.put(id, worker_con) worker_con.setblocking(False) except OSError as error: raise OSError('Error: {}, address: {}'.format( error, ip_addr)) writer.close()
def __run_deque(self): """ Sending thread function. """ writing_list = {} active_connections = [] while not self.Exit: if len(active_connections) == 0 or self.send_que.qsize() > 0: try: target, data = self.send_que.get(timeout=1) for send_to in target: fd = self.__connections.find(send_to) if fd is not None: buffer = writing_list.get(fd, BufferWriter()) writing_list[fd] = buffer # if can send if buffer.is_done(): pkg = { Key.Type: Type_Val.Normal, Key.From: self.__connections.get_id(), Key.To: target, Key.Content: data } # do encode buffer.set_content(pkg) active_connections.append(fd) # record length self.__data_bytes_sent.value += len(buffer) # put it back else: self.send_que.put(([send_to], data)) except Empty: continue # do send jobs if len(active_connections) > 0: _, writable, _ = select.select([], active_connections, [], 1) for fd in writable: try: buf = writing_list[fd] buf.send(fd) if buf.is_done(): active_connections.remove(fd) # ignore exceptions and left it for main thread to handle. except OSError: active_connections.remove(fd) for buf in writing_list.values(): buf.close()
def start_star_net( nodes: StarNetwork_Initialization_Package) -> ICommunication_Process: worker_register = Worker_Register() # register worker_register.register(Initialization_Server, nodes) data = { Key.Type: Type_Val.Submission, Key.From: Initialization_Server, Key.To: -1, Key.Content: nodes } writer = BufferWriter() for id, uuid, address in nodes: try: con = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # connect con.connect((address, STAR_NET_WORKING_PORTS)) # write key data[Key.To] = id # serialize and send writer.set_content(data) writer.send(con) # add worker_register.identify(id, uuid, con=con) except OSError as error: for con in worker_register.to_list(): if isinstance(con, socket.socket): con.close() raise OSError('Error: {}, while connecting {}.'.format( error, address)) writer.close() if worker_register.check(): com = Communication_Process(worker_register) return com else: raise OSError('Some of workers didnt respond properly.') # 解释一下 337 行代码。能够满足 len(active_connections) == 0 or self.send_que.qsize() > 0 条件的, # 要么是无数据可发,要么是有数据可从队列中取。当出现 Empty Exception 时,一定是无数据可发,即前一项满足 # 因此这时可以直接调用 continue 进入下一轮循环
def __call__(self, nodes: NodeAssignment) -> AbsCommunicationProcess: worker_register = WorkerRegister() # register worker_register.register(Initialization_Server, nodes) data = { Key.Type: Type_Val.Submission, Key.From: Initialization_Server, Key.To: -1, Key.Content: nodes } writer = BufferWriter() uuid = nodes.uuid for id, address in nodes: try: con = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # connect con.connect((address, STAR_NET_WORKING_PORTS)) # write key data[Key.To] = id # serialize and send writer.set_content(data) writer.send(con) # add worker_register.identify(id, uuid, con=con) except OSError as error: for con in worker_register.to_list(): if isinstance(con, socket.socket): con.close() raise OSError('{}, while connecting {}.'.format( error, address)) writer.close() if worker_register.check(): com = CommunicationProcess(worker_register) return com else: raise OSError('Some of workers didnt respond properly.')
def run(self): """ Bootstrap method start both sending and receiving thread on target socket object """ import threading recv_buffer_list = {} for fd in self.__connections.to_list(): recv_buffer_list[fd] = BufferReader() fd.setblocking(False) # handle writeable event here __write_thread = threading.Thread( target=self.__run_deque, name='Communication process -> deque thread.', daemon=True) __write_thread.start() # handle readable event here while not self.Exit: active_connections = self.__connections.to_list() if len(active_connections) == 0: self.Exit = True break readable, _, excepts = select.select(active_connections, [], active_connections, 1) # read for fd in readable: try: buf = recv_buffer_list[fd] buf.recv(fd) if buf.is_done(): # record length self.__data_bytes_received.value += len(buf) # do decode data = buf.get_content() _from = data[Key.From] self.recv_que.put((_from, data[Key.Content])) except OSError as error: recv_buffer_list[fd].close() addr = 'Unknown' try: addr = fd.getpeername() except Exception: pass self.__report_connection_lost(fd, addr) # # # print DEBUG message # import sys # import traceback # exc_type, exc_value, exc_tb = sys.exc_info() # traceback.print_exception(exc_type, exc_value, exc_tb) # # print DEBUG message # handle exception for fd in excepts: recv_buffer_list[fd].close() self.__report_connection_lost(fd, fd.raddr) # sleep(ICommunication_Process.Circle_interval) __write_thread.join() for fd in self.__connections.to_list(): BufferWriter.request_close(fd) fd.close() self.recv_que.close() self.send_que.close() self.__connections.reset() print('Communication process exited.')
def __send_proc(self): """ Sending thread function. """ writing_list = {} active_connections = [] tmp_item = None # if not exit and workable while not self.__exit_mark or not self.__send_queue.empty() or len(active_connections) != 0: # if network is idle, or queue has items if len(active_connections) == 0 or self.__send_queue.qsize() > 0: if isinstance(tmp_item, tuple): target, data = tmp_item # cond: (len(a) > 0 and buff is not valid and qsize > 0) tmp_item = None else: try: target, data = self.__send_queue.get( timeout=1) # cond: (len(a) == 0 and qsize == 0) or (qsize > 0) except queue.Empty: continue left = list() for send_to in target: fd = self.__connections.find(send_to) if fd is None: continue # cond: (send_to is valid) and (fd is valid) buffer = writing_list.get(fd, BufferWriter()) writing_list[fd] = buffer # if not full if buffer.is_done(): pkg = { Key.Type: Type_Val.Normal, Key.From: self.__connections.get_id(), Key.To: target, Key.Content: data } # cond: (buffer is valid) and (send_to is valid) and (fd is valid) # do encode buffer.set_content(pkg) active_connections.append(fd) # record length self.__data_bytes_sent += len(buffer) # put it back else: if fd not in active_connections: active_connections.append(fd) left.append(send_to) if len(left) > 0: # cond: (qfull or not qfull) and tmp_item = None tmp_item = (left, data) if not self.__send_queue.full(): try: self.__send_queue.put_nowait(tmp_item) # cond: put valid and len(left) > 0 tmp_item = None except queue.Full: pass # do send jobs if len(active_connections) > 0: try: _, writable, excepts = select.select([], active_connections, [], 1) except ValueError: continue for fd in writable: try: buf = writing_list[fd] buf.send(fd) if buf.is_done(): active_connections.remove(fd) # ignore exceptions and left it for main thread to handle. except OSError: active_connections.remove(fd) for fd in excepts: active_connections.remove(fd) for buf in writing_list.values(): buf.close()