class Pointer: def __init__(self, device='/dev/ttyUSB0', baud=115200): self.__hw = Protocol(device, baud) self.hid = self.get_id() self.calib = self.__get_calib() self.__pm = PointingModel(z1=self.calib[0], z2=self.calib[1], z3=self.calib[2]) self.target = EqCoords(0, 0) self.home() def get_id(self): return self.__hw.get_id() def home(self): return self.__hw.home() def __get_calib(self): return [self.__hw.get_calib(i) for i in range(3)] def set_calib(self, calib): if len(calib) != 3: raise ValueError("Wrong number of calibration values") for i, v in enumerate(calib): self.__hw.set_calib(i, v) def set_ref(self, eq=None, inst=None, t=0): self.__pm.set_ref(eq or self.target, inst or self.get_inst_coords(), t) def get_refs(self): """Return a list of dictionaries, each containing the observation time, equatorial and instrumental coordinates of a reference star""" eq = self.__pm.eq_refs inst = self.__pm.inst_refs t = self.__pm.t_refs refs = [] for i in range(self.__pm.get_nrefs()): refs.append({'eq': eq[i], 'inst': inst[i], 't': t[i]}) return refs def steps(self, ha, el): self.__hw.move(ha, el) def run(self, ha_dir, el_dir): if not -1 <= ha_dir <= 1: raise ValueError("ha_dir must be between -1 and 1") if not -1 <= el_dir <= 1: raise ValueError("el_dir must be between -1 and 1") half = STEPS/2 - 1 self.__hw.move(ha_dir*half, el_dir*half) def stop(self): self.__hw.stop() def enable_laser(self, enable): self.__hw.enable_laser(enable) def get_coords(self): return self.__pm.inst_to_eq(steps2rad(*self.__hw.get_pos())) def get_inst_coords(self): return Coords(*steps2rad(*self.__hw.get_pos())) def get_motor_pos(self): return self.__hw.get_pos() def goto(self, eq): self.target = eq self.__hw.goto(*rad2steps(*self.__pm.eq_to_inst(eq))) def close(self): self.__hw.close()
class _PeerClient: """端口代理转发客户端连接类:支持TCP和UDP映射 """ CONN_QUEUE_SIZE = 100 def __init__(self, server_host, sock_stream, client_id, client_name): self.server_host = socket.gethostbyname(server_host) self.client_id = client_id self.client_name = client_name self.tcp_e_address = sock_stream[1].get_extra_info('peername') self.udp_e_address = (None, None) # NAT external address of this client. self.tcp_maps = {} # {server_port: {...}, ...} self.udp_maps = {} # {server_port: {...}, ...} self.__conn_queue = asyncio.Queue() # {(server_port, reader, writer), ...} self.__protocol = Protocol(sock_stream) self.__task_serve_req = asyncio.create_task(self.__serve_request_task()) self.__task_process_conn = asyncio.create_task(self.__process_conn_task()) self.__task_watchdog = asyncio.create_task(self.__watchdog_task()) self.status = RunningStatus.RUNNING # PREPARE -> RUNNING (-> PENDING) -> STOPPED self.ping = time.time() # the period when the last tcp ping receives. self.timestamp = datetime.utcnow() # the period when proxy client creates. def tcp_statistic(self, upstream=None, downstream=None): if upstream is None: self.__protocol.tcp_statistic[0] = 0 else: self.__protocol.tcp_statistic[0] += upstream if downstream is None: self.__protocol.tcp_statistic[1] = 0 else: self.__protocol.tcp_statistic[1] += downstream return tuple(self.__protocol.tcp_statistic) def udp_statistic(self, upstream=None, downstream=None): if upstream is None: self.__protocol.udp_statistic[0] = 0 else: self.__protocol.udp_statistic[0] += upstream if downstream is None: self.__protocol.udp_statistic[1] = 0 else: self.__protocol.udp_statistic[1] += downstream return tuple(self.__protocol.udp_statistic) async def __tcp_service_task(self, server_port): async def tcp_service_handler(reader, writer): if self.status == RunningStatus.RUNNING and self.tcp_maps[server_port][TcpMapInfo.SWITCH]: await self.__conn_queue.put((server_port, reader, writer)) else: writer.close() server = await asyncio.start_server(tcp_service_handler, self.server_host, server_port) async with server: await server.wait_closed() async def __tcp_data_relay_task(self, server_port, sock_stream, peer_stream): async def data_relay_monitor(): while True: if self.status == RunningStatus.RUNNING and server_port in self.tcp_maps \ and self.tcp_maps[server_port][TcpMapInfo.SWITCH]: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) else: break async def simplex_data_relay(reader, writer, upstream=True): while True: try: data = await reader.read(Protocol.SOCKET_BUFFER_SIZE) if not data: break writer.write(data) await writer.drain() self.tcp_maps[server_port][TcpMapInfo.STATISTIC][0 if upstream else 1] += len(data) except IOError: break client_port = self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT] rp = peer_stream[1].get_extra_info("peername")[1] log.info(f'Server({server_port}) ----> {self.client_id}({client_port}) [{rp}]') _, pending = await asyncio.wait({ asyncio.create_task(data_relay_monitor()), asyncio.create_task(simplex_data_relay(sock_stream[0], peer_stream[1], upstream=True)), asyncio.create_task(simplex_data_relay(peer_stream[0], sock_stream[1], upstream=False)), }, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() sock_stream[1].close() peer_stream[1].close() log.info(f'Server({server_port}) --x-> {self.client_id}({client_port}) [{rp}]') async def __udp_service_task(self, server_port): udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_sock.setblocking(False) udp_sock.bind((self.server_host, server_port)) self.udp_maps[server_port][UdpMapInfo.UDP_SOCKET] = udp_sock while True: if self.status != RunningStatus.RUNNING or not self.udp_maps[server_port][TcpMapInfo.SWITCH]: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) continue try: data, address = udp_sock.recvfrom(Protocol.SOCKET_BUFFER_SIZE) if not data: raise IOError(f'EOF from {address}') except BlockingIOError: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) continue except Exception as e: log.error(e) break try: len_data = len(data) data_packet = Protocol.pack_udp_data_packet(server_port, address, data, pack_data=True) if self.udp_e_address: udp_sock.sendto(data_packet, self.udp_e_address) self.udp_statistic(Protocol.UDP_DATA_HEADER_LEN, 0) self.udp_maps[server_port][UdpMapInfo.STATISTIC][0] += len_data log.debug(f'Received UDP Packet on Port:{server_port}, Client:{address}, Size:{len_data}') except IOError as e: log.error(e) break except Exception as e: log.error(e) self.udp_maps[server_port][UdpMapInfo.SWITCH] = False self.udp_maps[server_port][UdpMapInfo.UDP_SOCKET].close() async def __process_conn_task(self): while True: try: server_port, reader, writer = await self.__conn_queue.get() except Exception as e: log.error(e) break else: self.__conn_queue.task_done() try: client_port = self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT] result = await self.__protocol.request( Protocol.ServerCommand.ADD_TCP_CONNECTION, client_port, timeout=Protocol.CONNECTION_TIMEOUT) status, detail = result.split(Protocol.PARAM_SEPARATOR, 1) if status == Protocol.Result.SUCCESS: conn_uuid = detail else: raise ProxyError(detail) wait_time = 0 while wait_time < Protocol.CONNECTION_TIMEOUT: if conn_uuid in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]: break await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) wait_time += Protocol.TASK_SCHEDULE_PERIOD if conn_uuid not in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]: raise ProxyError(f'{self.client_id}({client_port}) ----> Server({server_port}) ... [TIMEOUT].') reader2, writer2 = self.tcp_maps[server_port][TcpMapInfo.CONN_POOL][conn_uuid] del self.tcp_maps[server_port][TcpMapInfo.CONN_POOL][conn_uuid] asyncio.create_task(self.__tcp_data_relay_task(server_port, (reader, writer), (reader2, writer2))) except Exception as e: log.error(e) writer.close() async def __serve_request_task(self): while True: try: uuid, cmd, data = await self.__protocol.get_request(timeout=None) except Exception as e: log.error(e) break result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.SUCCESS, '')) try: if self.status != RunningStatus.RUNNING: # reject any client request. result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.INVALID, 'server is not running')) elif cmd == Protocol.Command.PING: self.ping = time.time() result += datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') elif cmd == Protocol.ClientCommand.CHECK_TCP_PORT: port = int(data) if check_listening('', port): result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.ERROR, 'tcp port is in use')) elif cmd == Protocol.ClientCommand.CHECK_UDP_PORT: port = int(data) if not check_udp_port_available(port): result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.ERROR, 'udp port is in use')) elif cmd == Protocol.ClientCommand.ADD_TCP_MAP: str_pair = data.split(Protocol.PARAM_SEPARATOR) c = int(str_pair[0]) s = int(str_pair[1]) await self.add_tcp_map(s, c) elif cmd == Protocol.ClientCommand.PAUSE_TCP_MAP: port = int(data) await self.pause_tcp_map(port) elif cmd == Protocol.ClientCommand.RESUME_TCP_MAP: port = int(data) await self.resume_tcp_map(port) elif cmd == Protocol.ClientCommand.REMOVE_TCP_MAP: port = int(data) await self.remove_tcp_map(port) elif cmd == Protocol.ClientCommand.ADD_UDP_MAP: str_pair = data.split(Protocol.PARAM_SEPARATOR) c = int(str_pair[0]) s = int(str_pair[1]) await self.add_udp_map(s, c) elif cmd == Protocol.ClientCommand.PAUSE_UDP_MAP: port = int(data) await self.pause_udp_map(port) elif cmd == Protocol.ClientCommand.RESUME_UDP_MAP: port = int(data) await self.resume_udp_map(port) elif cmd == Protocol.ClientCommand.REMOVE_UDP_MAP: port = int(data) await self.remove_udp_map(port) elif cmd == Protocol.ClientCommand.PAUSE_PROXY: await self.pause_client() elif cmd == Protocol.ClientCommand.RESUME_PROXY: await self.resume_client() elif cmd == Protocol.ClientCommand.DISCONNECT: await self.close_client() else: log.warning(f'Unrecognised request from {self.client_name}(CID:{self.client_id}): ' f'{Protocol.make_req(uuid, cmd, data)}') result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.INVALID, 'unrecognised request')) except Exception as e: log.error(f'Error while processing request({Protocol.make_req(uuid, cmd, data)}) ' f'from {self.client_name}(CID:{self.client_id}): {e}') result = Protocol.PARAM_SEPARATOR.join((Protocol.Result.ERROR, 'error while processing request')) finally: try: await self.__protocol.send_response(uuid, cmd, result) except Exception as e: log.error(e) break async def __watchdog_task(self): await self.__protocol.wait_closed() log.warning(f'Stopping {self.client_name}(CID:{self.client_id}) by watchdog ...') for server_port in list(self.tcp_maps.keys()): await self.remove_tcp_map(server_port) for server_port in list(self.udp_maps.keys()): await self.remove_udp_map(server_port) while not self.__conn_queue.empty(): stream = self.__conn_queue.get_nowait() stream[1].close() self.__conn_queue.task_done() self.__conn_queue = None self.__task_serve_req.cancel() self.__task_process_conn.cancel() self.status = RunningStatus.STOPPED log.warning(f'{self.client_name}(CID:{self.client_id}) is STOPPED by watchdog !!!') def _map_msg(self, server_port, client_port, status, extra=None): return f'{ sys._getframe(1).f_code.co_name.upper() }: ' \ f'Server({server_port}) >>> {self.client_id}({client_port}) ... ' \ f'[{ status.upper() }]{ " (" + extra + ")" if extra else "" }' async def add_tcp_map(self, server_port, client_port): if server_port in self.tcp_maps: raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'already registered.')) if check_listening(self.server_host, server_port): raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'target port is in use.')) self.tcp_maps[server_port] = { TcpMapInfo.CLIENT_PORT: client_port, TcpMapInfo.SWITCH: True, TcpMapInfo.STATISTIC: [0, 0], TcpMapInfo.CREATE_TIME: datetime.utcnow(), TcpMapInfo.CONN_POOL: {}, TcpMapInfo.TASK_LISTEN: asyncio.create_task(self.__tcp_service_task(server_port)), } log.success(self._map_msg(server_port, client_port, 'OK')) async def pause_tcp_map(self, server_port): if server_port not in self.tcp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) self.tcp_maps[server_port][TcpMapInfo.SWITCH] = False log.success(self._map_msg(server_port, self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT], 'OK')) async def resume_tcp_map(self, server_port): if server_port not in self.tcp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) self.tcp_maps[server_port][TcpMapInfo.SWITCH] = True log.success(self._map_msg(server_port, self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT], 'OK')) async def remove_tcp_map(self, server_port): if server_port not in self.tcp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) await self.pause_tcp_map(server_port) for stream in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]: stream[1].close() self.tcp_maps[server_port][TcpMapInfo.TASK_LISTEN].cancel() log.success(self._map_msg(server_port, self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT], 'OK')) del self.tcp_maps[server_port] async def add_udp_map(self, server_port, client_port): if server_port in self.udp_maps: raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'already registered.')) if not check_udp_port_available(server_port): raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'target port is in use.')) self.udp_maps[server_port] = { UdpMapInfo.CLIENT_PORT: client_port, UdpMapInfo.UDP_SOCKET: None, UdpMapInfo.SWITCH: True, UdpMapInfo.STATISTIC: [0, 0], UdpMapInfo.CREATE_TIME: datetime.utcnow(), UdpMapInfo.TASK_TRANSFER: asyncio.create_task(self.__udp_service_task(server_port)), } log.success(self._map_msg(server_port, client_port, 'OK')) async def pause_udp_map(self, server_port): if server_port not in self.udp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) self.udp_maps[server_port][UdpMapInfo.SWITCH] = False log.success(self._map_msg(server_port, self.udp_maps[server_port][UdpMapInfo.CLIENT_PORT], 'OK')) async def resume_udp_map(self, server_port): if server_port not in self.udp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) self.udp_maps[server_port][UdpMapInfo.SWITCH] = True log.success(self._map_msg(server_port, self.udp_maps[server_port][UdpMapInfo.CLIENT_PORT], 'OK')) async def remove_udp_map(self, server_port): if server_port not in self.udp_maps: raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.')) await self.pause_udp_map(server_port) self.udp_maps[server_port][UdpMapInfo.UDP_SOCKET].close() self.udp_maps[server_port][UdpMapInfo.TASK_TRANSFER].cancel() log.success(self._map_msg(server_port, self.udp_maps[server_port][UdpMapInfo.CLIENT_PORT], 'OK')) del self.udp_maps[server_port] async def pause_client(self): msg = f'PAUSE_CLIENT: {self.client_name}(CID:{self.client_id}) ... ' if self.status == RunningStatus.STOPPED: raise ProxyError(msg + '[ERROR] (already stopped.)') self.status = RunningStatus.PENDING log.success(msg + '[OK]') async def resume_client(self): msg = f'RESUME_CLIENT: {self.client_name}(CID:{self.client_id}) ... ' if self.status == RunningStatus.STOPPED: raise ProxyError(msg + '[ERROR] (already stopped.)') self.status = RunningStatus.RUNNING log.success(msg + '[OK]') async def close_client(self, wait=True): self.__protocol.close() if wait: while self.status != RunningStatus.STOPPED: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD)
PORT = 8888 x = 131 # only Alice knows this s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) prot = Protocol(s) prot.open(PORT) bobSends, addr, _ = prot.read() publicKey = (int(bobSends)**x) % n aliceSends = (g**x) % n prot.write(str(aliceSends), addr) while 1: data, addr, error = prot.sread(publicKey) if error == 1: print('Error code 1. Data does not corresponds to checksum') else: if not data: break reply = data prot.swrite(reply, addr, publicKey) print(addr[0], str(addr[1]), data) prot.close()
print('Command line: getweather.py serial_port serial_speed') sys.exit(1) currenttime = time.time() db = DBHelper(dbFileName) device = Protocol(serialPort, baudRate, logEnabled) if device.ping(deviceAddress): pressure, sernumP = device.getPressure(deviceAddress) if 10 < pressure < 1000: print('Pressure - ' + str(pressure) + ' mmHg') pressureSensorId = db.getSensorId(pressureSensorType, sernumP) db.storeValue(currenttime, pressure, pressureSensorId) humidity, sernumH = device.getHumidity(deviceAddress) if not math.isnan(humidity): print('Humidity - ' + str(humidity) + '%') humiditySensorID = db.getSensorId(humiditySensorType, sernumH) db.storeValue(currenttime, humidity, humiditySensorID) values = device.getTemp(deviceAddress) i = 1 for (temperature, sn) in values: print('T' + str(i) + ' - ' + "%.1f" % temperature + ' C, sensor'), device.printPacket(sn) i += 1 termSensorId = db.getSensorId(termSensorType, sn) db.storeValue(currenttime, temperature, termSensorId) device.close() db.updateAvgTables() db.updateAllRecordsView() db.close()
elif opt in ("-i", "--ip_addr"): ip_addr = arg elif opt in ('-p', '--port_num'): port = arg elif opt in ('-p', '--payload_len'): payload_len = arg file_name = 'doc2.txt' # File-to-send payload_len = 512 # Payload length ip_addr = 'localhost' # Receiver's ID address port = 8080 # Receiver's port number from protocol import Protocol from adaptors import * if __name__ == '__main__': parse_args(sys.argv[1:]) client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client.setblocking(False) client.bind(('0.0.0.0', 0)) client_protocol = Protocol(UdtAdaptor(client, (ip_addr, port)), server=False, name='sender') with open(file_name, 'rb') as f: data = f.read() client_protocol.send(data) client_protocol.flush() client_protocol.close()
class Pointer: def __init__(self, device='/dev/ttyUSB0', baud=115200): self.__hw = Protocol(device, baud) self.hid = self.get_id() self.calib = self.__get_calib() self.__pm = PointingModel(z1=self.calib[0], z2=self.calib[1], z3=self.calib[2]) self.target = EqCoords(0, 0) self.home() def get_id(self): return self.__hw.get_id() def home(self): return self.__hw.home() def __get_calib(self): return [self.__hw.get_calib(i) for i in range(3)] def set_calib(self, calib): if len(calib) != 3: raise ValueError("Wrong number of calibration values") for i, v in enumerate(calib): self.__hw.set_calib(i, v) def set_ref(self, eq=None, inst=None, t=0): self.__pm.set_ref(eq or self.target, inst or self.get_inst_coords(), t) def get_refs(self): """Return a list of dictionaries, each containing the observation time, equatorial and instrumental coordinates of a reference star""" eq = self.__pm.eq_refs inst = self.__pm.inst_refs t = self.__pm.t_refs refs = [] for i in range(self.__pm.get_nrefs()): refs.append({'eq': eq[i], 'inst': inst[i], 't': t[i]}) return refs def steps(self, ha, el): self.__hw.move(ha, el) def run(self, ha_dir, el_dir): if not -1 <= ha_dir <= 1: raise ValueError("ha_dir must be between -1 and 1") if not -1 <= el_dir <= 1: raise ValueError("el_dir must be between -1 and 1") half = STEPS / 2 - 1 self.__hw.move(ha_dir * half, el_dir * half) def stop(self): self.__hw.stop() def enable_laser(self, enable): self.__hw.enable_laser(enable) def get_coords(self): return self.__pm.inst_to_eq(steps2rad(*self.__hw.get_pos())) def get_inst_coords(self): return Coords(*steps2rad(*self.__hw.get_pos())) def get_motor_pos(self): return self.__hw.get_pos() def goto(self, eq): self.target = eq self.__hw.goto(*rad2steps(*self.__pm.eq_to_inst(eq))) def close(self): self.__hw.close()
print ('Command line: getweather.py serial_port serial_speed') sys.exit(1) currenttime = time.time() db = DBHelper(dbFileName) device = Protocol(serialPort, baudRate, logEnabled) if device.ping(deviceAddress): pressure, sernumP = device.getPressure(deviceAddress) if 10 < pressure < 1000: print ('Pressure - ' + str(pressure) + ' mmHg') pressureSensorId = db.getSensorId(pressureSensorType, sernumP) db.storeValue(currenttime, pressure, pressureSensorId) humidity, sernumH = device.getHumidity(deviceAddress) if not math.isnan(humidity): print ('Humidity - ' + str(humidity) + '%') humiditySensorID = db.getSensorId(humiditySensorType, sernumH) db.storeValue(currenttime, humidity, humiditySensorID) values = device.getTemp(deviceAddress) i = 1 for (temperature, sn) in values: print ('T' + str(i) + ' - ' + "%.1f" % temperature + ' C, sensor'), device.printPacket(sn) i += 1 termSensorId = db.getSensorId(termSensorType, sn) db.storeValue(currenttime, temperature, termSensorId) device.close() db.updateAvgTables() db.updateAllRecordsView() db.close()
class ProxyClient: """端口代理转发类(客户端) """ def __init__(self, server_host, server_port=10000, port_map=None, cid=None, name='DefaultClient'): for maps in (port_map['TCP'], port_map['UDP']): for p in maps: if type(p) != int or type(maps[p]) != int or list( maps.values()).count(maps[p]) != 1: raise ValueError('Invalid argument for `port_map`: %s' % port_map) server_host = socket.gethostbyname(server_host) server_port = int(server_port) self.client_id = cid # generates by server after connection if not provided. self.client_name = name # user defined client name. self.server_host = server_host # server side host address. self.server_port = server_port # for both tcp and udp service. self.ini_tcp_map = port_map['TCP'] # = {80: 10080, 81: 10081, ...} self.ini_udp_map = port_map['UDP'] # = {80: 10080, ...} self.tcp_maps = None # tcp proxy map self.udp_maps = None # udp proxy map self.__udp_socket = None # socket for receiving udp packet from server to local udp service. self.__udp_req_map = None # socket for feedback udp packet from local udp service to server. self.__protocol = None # tcp connection used to communicate with server. self.__task_serve_req = None # task for processing requests form server. self.__task_udp_receive = None # task for receiving udp packet form server. self.__task_udp_feedback = None # task for feedback udp packet to server. self.__task_daemon = None # task for daemon self.status = RunningStatus.PREPARE # PREPARE -> RUNNING (-> PENDING) -> STOPPED self.ping = time.time() # the period when the last udp ping receives. self.timestamp = datetime.utcnow( ) # the period when proxy client creates. def tcp_statistic(self, upstream=None, downstream=None): if upstream is None: self.__protocol.tcp_statistic[0] = 0 else: self.__protocol.tcp_statistic[0] += upstream if downstream is None: self.__protocol.tcp_statistic[1] = 0 else: self.__protocol.tcp_statistic[1] += downstream return tuple(self.__protocol.tcp_statistic) def udp_statistic(self, upstream=None, downstream=None): if upstream is None: self.__protocol.udp_statistic[0] = 0 else: self.__protocol.udp_statistic[0] += upstream if downstream is None: self.__protocol.udp_statistic[1] = 0 else: self.__protocol.udp_statistic[1] += downstream return tuple(self.__protocol.udp_statistic) def _map_msg(self, client_port, server_port, status, extra=None): return f'{ sys._getframe(1).f_code.co_name.upper() }: ' \ f'Local({client_port}) >>> {self.server_host}({server_port}) ... ' \ f'[{ status.upper() }]{ " (" + extra + ")" if extra else "" }' async def add_tcp_map(self, client_port, server_port): if client_port in self.tcp_maps: raise ProxyError( self._map_msg(client_port, server_port, 'ERROR', f'already registered.')) status, detail = (await self.__protocol.request( Protocol.ClientCommand.ADD_TCP_MAP, f'{client_port}{Protocol.PARAM_SEPARATOR}{server_port}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError( self._map_msg(client_port, server_port, 'ERROR', detail)) self.tcp_maps[client_port] = { TcpMapInfo.SERVER_PORT: server_port, TcpMapInfo.SWITCH: True, TcpMapInfo.STATISTIC: [0, 0], TcpMapInfo.CREATE_TIME: datetime.utcnow(), } log.success(self._map_msg(client_port, server_port, 'OK')) async def pause_tcp_map(self, client_port): if client_port not in self.tcp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.PAUSE_TCP_MAP, f'{self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) self.tcp_maps[client_port][TcpMapInfo.SWITCH] = False log.success(self._map_msg(client_port, server_port, 'OK')) async def resume_tcp_map(self, client_port): if client_port not in self.tcp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.RESUME_TCP_MAP, f'{self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) self.tcp_maps[client_port][TcpMapInfo.SWITCH] = True log.success(self._map_msg(client_port, server_port, 'OK')) async def remove_tcp_map(self, client_port): if client_port not in self.tcp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.REMOVE_TCP_MAP, f'{self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) del self.tcp_maps[client_port] log.success(self._map_msg(client_port, server_port, 'OK')) async def add_udp_map(self, client_port, server_port): if client_port in self.udp_maps: raise ProxyError( self._map_msg(client_port, server_port, 'ERROR', f'already registered.')) status, detail = (await self.__protocol.request( Protocol.ClientCommand.ADD_UDP_MAP, f'{client_port}{Protocol.PARAM_SEPARATOR}{server_port}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) self.udp_maps[client_port] = { UdpMapInfo.SERVER_PORT: server_port, UdpMapInfo.SWITCH: True, UdpMapInfo.STATISTIC: [0, 0], UdpMapInfo.CREATE_TIME: datetime.utcnow(), } log.success(self._map_msg(client_port, server_port, 'OK')) async def pause_udp_map(self, client_port): if client_port not in self.udp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.udp_maps[client_port][UdpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.PAUSE_UDP_MAP, f'{self.udp_maps[client_port][UdpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) self.udp_maps[client_port][UdpMapInfo.SWITCH] = False log.success(self._map_msg(client_port, server_port, 'OK')) async def resume_udp_map(self, client_port): if client_port not in self.udp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.udp_maps[client_port][UdpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.RESUME_UDP_MAP, f'{self.udp_maps[client_port][UdpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) self.udp_maps[client_port][UdpMapInfo.SWITCH] = True log.success(self._map_msg(client_port, server_port, 'OK')) async def remove_udp_map(self, client_port): if client_port not in self.udp_maps: raise ProxyError( self._map_msg(client_port, None, 'ERROR', f'not registered.')) else: server_port = self.udp_maps[client_port][UdpMapInfo.SERVER_PORT] status, detail = (await self.__protocol.request( Protocol.ClientCommand.REMOVE_UDP_MAP, f'{self.udp_maps[client_port][UdpMapInfo.SERVER_PORT]}', Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1) if status != Protocol.Result.SUCCESS: raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'), detail) del self.udp_maps[client_port] log.success(self._map_msg(client_port, server_port, 'OK')) async def __tcp_data_relay_task(self, client_port, sock_stream, peer_stream): async def data_relay_monitor(): while True: if self.status == RunningStatus.RUNNING and client_port in self.tcp_maps \ and self.tcp_maps[client_port][TcpMapInfo.SWITCH]: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) else: break async def simplex_data_relay(reader, writer, upstream=True): while True: try: data = await reader.read(Protocol.SOCKET_BUFFER_SIZE) if not data: break writer.write(data) await writer.drain() self.tcp_maps[client_port][TcpMapInfo.STATISTIC][ 0 if upstream else 1] += len(data) except IOError: break server_port = self.tcp_maps[client_port][TcpMapInfo.SERVER_PORT] rp = peer_stream[1].get_extra_info("sockname")[1] log.info( f'Server({server_port}) ----> {self.client_id}({client_port}) [{rp}]' ) _, pending = await asyncio.wait( { asyncio.create_task(data_relay_monitor()), asyncio.create_task( simplex_data_relay( sock_stream[0], peer_stream[1], upstream=True)), asyncio.create_task( simplex_data_relay( peer_stream[0], sock_stream[1], upstream=False)), }, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() sock_stream[1].close() peer_stream[1].close() log.info( f'Server({server_port}) --x-> {self.client_id}({client_port}) [{rp}]' ) async def __serve_request_task(self): while True: uuid, cmd, data = await self.__protocol.get_request(timeout=None) result = Protocol.PARAM_SEPARATOR.join( (Protocol.Result.SUCCESS, '')) try: if cmd == Protocol.Command.PING: result += datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') elif cmd == Protocol.ServerCommand.ADD_TCP_CONNECTION: client_port = int(data) server_port = self.tcp_maps[client_port][ TcpMapInfo.SERVER_PORT] reader1, writer1 = await asyncio.open_connection( '127.0.0.1', client_port) reader2, writer2 = await asyncio.open_connection( self.server_host, self.server_port) conn_uuid = uuid4().hex identify = Protocol.PARAM_SEPARATOR.join( (Protocol.ConnectionType.PROXY_TCP_DATA, self.client_id, str(server_port), conn_uuid)) writer2.write((identify + '\n').encode()) await writer2.drain() resp = (await reader2.readline()).decode().strip() if not resp or resp[:len(Protocol.Result.SUCCESS )] != Protocol.Result.SUCCESS: raise ProxyError( 'Add tcp connection failed while connecting to server.' ) asyncio.create_task( self.__tcp_data_relay_task(client_port, (reader1, writer1), (reader2, writer2))) result += conn_uuid else: log.warning( f'Unrecognised request from Server: {Protocol.make_req(uuid, cmd, data)}' ) result = Protocol.PARAM_SEPARATOR.join( (Protocol.Result.INVALID, 'unrecognised request')) except Exception as e: log.error( f'Error while processing request({Protocol.make_req(uuid, cmd, data)}): {e}' ) result = Protocol.PARAM_SEPARATOR.join( (Protocol.Result.ERROR, str(e))) finally: try: await self.__protocol.send_response(uuid, cmd, result) except Exception as e: log.error(e) break async def __udp_receive_task(self): def udp_send_data(__b_data, __udp_port, __user_address): key = hash((__udp_port, __user_address)) if key in self.__udp_req_map: sock = self.__udp_req_map[key][2] self.__udp_req_map[key][3] = time.time() else: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setblocking(False) self.__udp_req_map[key] = [ __udp_port, user_address, sock, time.time() ] sock.sendto(__b_data, ('127.0.0.1', __udp_port)) while True: try: data, address = self.__udp_socket.recvfrom( Protocol.SOCKET_BUFFER_SIZE) except BlockingIOError: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) continue except IOError as e: log.error(e) break try: # if address[0] != self.server_host: # raise ProxyError(f'Received udp packet from {address} which is not the server.') try: packet_info = Protocol.unpack_udp_packet(data, unpack_data=False) except Exception as e: raise ProxyError(f'Protocol.unpack_udp_packet(): {e}') if packet_info[Protocol.UdpPacketInfo. TYPE] == Protocol.UdpPacketType.SYNC: self.udp_statistic(0, len(data)) self.__udp_ping = time.time() log.debug( f'UDP_PING: {packet_info[Protocol.UdpPacketInfo.TIMESTAMP]}' ) elif packet_info[Protocol.UdpPacketInfo. TYPE] == Protocol.UdpPacketType.DATA: self.udp_statistic(0, Protocol.UDP_DATA_HEADER_LEN) self.udp_maps[UdpMapInfo.STATISTIC][1] += len( data) - Protocol.UDP_DATA_HEADER_LEN user_address = packet_info[ Protocol.UdpPacketInfo.USER_ADDRESS] server_port = packet_info[ Protocol.UdpPacketInfo.SERVER_PORT] client_port = None for port in self.udp_maps: if self.udp_maps[port][ UdpMapInfo.SERVER_PORT] == server_port: client_port = port break if not client_port: log.warning( f'Received udp data packet on server port({server_port}) that not registered.' ) continue try: udp_send_data(data[Protocol.UDP_DATA_HEADER_LEN:], client_port, user_address) except IOError as e: log.error(e) continue log.debug( f'Received udp data packet from {user_address} on port({client_port})' ) else: self.udp_statistic(0, len(data)) raise ProxyError( f'Received udp packet from {address} with unknown type.' ) except Exception as e: log.debug(f'Received udp packet from: {address}, data:\n' + data.hex()) log.warning(e) self.__protocol.close() async def __udp_feedback_task(self): while True: for key in list(self.__udp_req_map): client_port, user_address, sock, _ = self.__udp_req_map[key] try: while True: try: data = sock.recv(Protocol.SOCKET_BUFFER_SIZE) except BlockingIOError: break data_packet = Protocol.pack_udp_data_packet( self.udp_maps[client_port][UdpMapInfo.SERVER_PORT], user_address, data, pack_data=True) sock.sendto(data_packet, (self.server_host, self.server_port)) self.udp_statistic(Protocol.UDP_DATA_HEADER_LEN, 0) self.udp_maps[UdpMapInfo.STATISTIC][0] += len(data) log.debug( f'Sent feedback to server port({self.server_port})' ) except IOError as e: log.error(e) val = self.__udp_req_map.pop(key) val[2].close() await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) async def __daemon_task(self): cnt = 0 await asyncio.sleep(Protocol.CLIENT_UDP_PING_PERIOD) while True: # # Send tcp keep alive message. # if not (cnt % Protocol.CLIENT_TCP_PING_PERIOD): try: await self.__protocol.request( Protocol.Command.PING, timeout=Protocol.CONNECTION_TIMEOUT) except Exception as e: log.error(e) break # # Send udp keep alive data packet. # if not (cnt % Protocol.CLIENT_UDP_PING_PERIOD): data_packet = Protocol.pack_udp_sync_packet( client_id=self.client_id) try: self.__udp_socket.sendto( data_packet, (self.server_host, self.server_port)) except IOError as e: log.error(e) # log.debug(f'Sent UDP ping packet to server({self.server_host}:{self.server_port})') # # Check if udp keep alive data packet received in time. # if time.time( ) - self.__udp_ping > Protocol.UDP_UNREACHABLE_WARNING_PERIOD: log.warning('Too many udp ping packet lost!') self.__udp_ping += Protocol.CLIENT_UDP_PING_PERIOD # # Remove virtual udp connections in `self.__udp_req_map` which is too old. # for key in list(self.__udp_req_map.keys()): if time.time( ) - self.__udp_req_map[key][3] > Protocol.UDP_REQUEST_DURATION: val = self.__udp_req_map.pop(key) val[2].close() # # Every loop comes with a rest. # cnt += 1 await asyncio.sleep(1) self.__protocol.close() # kill itself. async def __main_task(self): if self.status == RunningStatus.RUNNING or self.status == RunningStatus.PENDING: log.error( 'Abort starting up the Proxy Client as it is already running.') return log.info( f'Connecting with the Proxy Server({self.server_host}:{self.server_port}) ...' ) reader, writer = await asyncio.open_connection(self.server_host, self.server_port) identify = Protocol.PARAM_SEPARATOR.join( (Protocol.ConnectionType.PROXY_CLIENT, self.client_id if self.client_id else "", self.client_name)) writer.write((identify + '\n').encode()) await writer.drain() resp = (await reader.readline()).decode().strip().split( Protocol.PARAM_SEPARATOR) if resp and resp[0] == Protocol.Result.SUCCESS: self.client_id = resp[1] log.success(f'Connected!') else: log.error(f'Failed!') return self.tcp_maps = {} self.udp_maps = {} self.__udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.__udp_socket.setblocking(False) self.__udp_req_map = {} self.__udp_ping = time.time() self.__protocol = Protocol((reader, writer)) self.__task_serve_req = asyncio.create_task( self.__serve_request_task()) self.__task_udp_receive = asyncio.create_task( self.__udp_receive_task()) self.__task_udp_feedback = asyncio.create_task( self.__udp_feedback_task()) self.__task_daemon = asyncio.create_task(self.__daemon_task()) self.status = RunningStatus.RUNNING log.success('>>>>>>>> Proxy Client is now STARTED !!! <<<<<<<<') try: for tcp_port in self.ini_tcp_map: await self.add_tcp_map(tcp_port, self.ini_tcp_map[tcp_port]) for udp_port in self.ini_udp_map: await self.add_udp_map(udp_port, self.ini_udp_map[udp_port]) except Exception as e: log.error(e) self.__protocol.close() await self.__protocol.wait_closed() log.warning('Stopping the Proxy Client ...') self.__task_serve_req.cancel() self.__task_udp_receive.cancel() self.__task_udp_feedback.cancel() self.__task_daemon.cancel() for key in self.__udp_req_map: self.__udp_req_map[key][2].close() self.__udp_socket.close() self.status = RunningStatus.STOPPED log.warning('>>>>>>>> Proxy Client is now STOPPED !!! <<<<<<<<') async def startup(self): asyncio.create_task(self.__main_task()) async def shutdown(self): log.warning(f'Stopping the Proxy Client by calling ...') self.__protocol.close() while self.status != RunningStatus.STOPPED: await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD) async def pause(self): if Protocol.Result.SUCCESS == await self.__protocol.request( Protocol.ClientCommand.PAUSE_PROXY, '', Protocol.CONNECTION_TIMEOUT): self.status = RunningStatus.PENDING log.warning('>>>>>>>> Proxy Client is now PENDING ... <<<<<<<<') else: log.error('Failed to pause the Proxy Client.') async def resume(self): if Protocol.Result.SUCCESS == await self.__protocol.request( Protocol.ClientCommand.RESUME_PROXY, '', Protocol.CONNECTION_TIMEOUT): self.status = RunningStatus.RUNNING log.success('>>>>>>>> Proxy Client is now RESUMED ... <<<<<<<<') else: log.error('Failed to resume the Proxy Client.') def run(self, debug=False): asyncio.run(self.__main_task(), debug=debug)
log.info("Starting up.") p = Protocol(args.device) time.sleep(2) p.setMotor(0, 2, 0) p.setMotor(1, 2, 0) p.setServo(0, 450) p.setServo(1, 450) p.getCS() p.getEN() # Run while not ctrl-c'd run = True while run: try: p.handle() k = p.read() if k != None: print k except KeyboardInterrupt: run = False print "Caught CTRL+C, stopping ..." # All done, clean up. p.close() log.info("All done.") if args.logfile: log_out.close() exit(0)