def run(self): log.info('rtk thread: start') # threads self.server = ServerThread(self.listen_port) self.controller = ControlThread(self.control_port, self.got_command_cb) # station_mode 指基站的模式,本地的模式与之相反 if self.station_mode == 'server': # 基站为 server, 本地为 client self.station = StationClientThread(self.name, self.config, self.got_data_cb, self.update_status_cb) else: # 基站为 client, 本地为 server self.station = StationServerThread(self.name, self.config, self.got_data_cb, self.update_status_cb) self.server.start() self.controller.start() self.station.start() # wait while self.running and self.server.running and self.station.running: time.sleep(2) # quit & clean up self.stop_thread('controller', self.controller) self.stop_thread('station', self.station) self.stop_thread('server', self.server) self.update_status_cb(RtkStatus.S_TERMINATED) log.info('rtk thread: bye') self.running = False
def wait_for_keyboard(self): """quit when press q or press ctrl-c, or exception from other threads""" try: while True: time.sleep(2) # 减少死锁概率 print("enter 'q' to quit, 'r' to reload, 'l' to list ports.") key = input().lower().strip() if key == 'q': break elif key == 'r': log.info('main: reload config.') self.start_threads_from_config() elif key == 'l': try: print('name, station_port, dispatch_port, control_port') for name, config in sorted(self.configs['entry'].items()): control_port = str(config['controlPort']) if 'controlPort' in config else None print('%s, %d, %d, %s' % (name, config['stationPort'], config['listenPort'], control_port)) except Exception as e: print('Error when list config: %s' % e) except KeyboardInterrupt: pass except (EOFError, OSError): # no input signal.signal(signal.SIGINT, self.exit_by_signal) signal.signal(signal.SIGTERM, self.exit_by_signal) while not self.is_interrupt: time.sleep(1)
def run(self): """线程主函数 循环运行,接受新的客户端的连接。 """ log.info('station server thread: start, port: %d' % self.port) try: server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if os.name != 'nt': server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', self.port)) server.listen(1) server.settimeout(3) # timeout: 3s while self.running: try: conn, address = server.accept() conn.settimeout(3) log.debug('new station connection from: %s' % str(address)) self.got_client(conn, str(address)) except socket.timeout: pass self.check_new_connections() # clean up server.close() self.disconnect() log.info('station server thread: bye') except Exception as e: log.error('station server thread error: %s' % e) self.running = False self.disconnect()
def wait_for_keyboard(self): """quit when press q or press ctrl-c, or exception from other threads""" try: while True: time.sleep(2) # 减少死锁概率 print("enter 'q' to quit, 'r' to reload, 'l' to list ports.") key = input().lower().strip() if key == 'q': break elif key == 'r': log.info('main: reload config.') configs = config_loader.load_config() self.configs = configs self.rtk_mgr.start_threads_from_config(self.configs) elif key == 'l': try: print( 'name, station_port, dispatch_port, control_port') for name, config in sorted( self.configs['entry'].items()): print( '%s, %d, %d, %s' % (name, config.station_port, config.listen_port, str(config.control_port))) except Exception as e: print('Error when list config: %s' % e) except KeyboardInterrupt: pass except (EOFError, OSError): # no input signal.signal(signal.SIGINT, self.exit_by_signal) signal.signal(signal.SIGTERM, self.exit_by_signal) while not self.is_interrupt: time.sleep(1)
def run(self): """线程主函数 循环运行,接受新的客户端的连接。 """ log.info('server thread: start, port: %d' % self.port) try: server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if os.name != 'nt': server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', self.port)) server.listen(100) # 并发 server.settimeout(1) # timeout: 1s while self.running: # 接受连接 try: conn, address = server.accept() conn.settimeout(3) self.dispatcher.add_client(conn, address) log.debug('new client from: %s' % str(address)) except socket.timeout: pass # 分发数据 self.dispatcher.dispatch() server.close() self.dispatcher.close_all_clients() log.info('server thread: bye') except Exception as e: log.error('server thread error: %s' % e) self.running = False
def run(self): """线程主函数 循环运行,接受到控制端口的 socket 连接(唯一),接收命令。 """ if self.port is None: return log.info('control thread: start, port: %d' % self.port) try: # 开始监听 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', self.port)) server.listen(1) server.settimeout(3) # timeout: 3s # 循环运行,监听端口,接收命令 while self.running: self.accept_client(server) self.receive_command() self.resolve_command() self.send_message() server.close() self.disconnect_client() log.info('control thread: bye') except Exception as e: log.error('control thread error: %s' % e) self.running = False
def resolve_command(self): """解析来自控制端口的数据 首先寻找指令起始标记,找不到就从 buffer 头删除一个字节,再继续寻找。 然后调用 resolve_command_from_begin() 从 buffer 的开头开始解析,直到遇到结尾标记为止。 """ if self.is_transparency and len(self.buffer) > 0: # 透传模式 command = b'send:' + bytes(self.buffer[:]) self.buffer.clear() log.info('control command: %s' % command) self.got_command_cb(command) while len(self.buffer) >= 4: # 解析指令 try: if self.is_in_command: # 指令从 buffer[0] 开始 command = self.resolve_command_from_begin() if self.is_in_command: # 没有遇到结尾,收到的指令还不完整 break elif command is not None: # 收到了指令 log.info('control command: %s' % command) self.got_command_cb(command) if command == b'%MODE1': # 透传模式 self.is_transparency = True break elif self.buffer[:4] == CMD_BEGIN: # begin of command self.is_in_command = True else: del self.buffer[0] # 出队一个字节 except Exception as e: log.warning('control thread resolve: %s' % e)
def check_new_connections(self): """检查新建的连接是否通过了握手测试""" new_connections = self.new_connections[:] time_sec = time.time() for i in range(0, len(new_connections)): connection_thread, established_time_sec = new_connections[i] # 逐个检查新建立的连接,判断是否握手成功、是否超时 if connection_thread.handshake_ok: # 握手成功 if self.connection_thread is not None and self.connection_thread.is_alive(): # stop old log.info('stopping existing station connection thread.') self.connection_thread.got_data_cb = lambda data: None self.connection_thread.update_status_cb = lambda status: None self.connection_thread.running = False # 不用等待 # set new connection_thread self.connection_thread = connection_thread self.connection_thread.got_data_cb = self.got_data_cb self.connection_thread.update_status_cb = self.update_status_cb self.new_connections.remove((connection_thread, established_time_sec)) elif (time_sec - established_time_sec > handshake_timeout_second) or time_sec < established_time_sec: # 超时 connection_thread.running = False # 不用等待 connection_thread.got_data_cb = lambda data: None connection_thread.update_status_cb = lambda status: None self.new_connections.remove((connection_thread, established_time_sec))
def run(self): """线程主函数 循环运行,接收来自客户端的数据并丢弃,向客户端发送 data_queue 中的数据包。 当 data_queue 过长时,丢弃旧的数据包。 """ log.info('station connection thread: start, %s' % self.address) self.update_status_cb(RtkStatus.S_CONNECTED) try: self.send_and_receive_data() except Exception as e: log.error('station connection thread error: %s' % e) self.update_status_cb(RtkStatus.S_DISCONNECTED) log.info('station connection thread: bye')
def run(self): """线程主函数 启动定时器,用于检查心跳超时 循环运行,接受新的客户端的连接。 """ log.info('http thread: start, port: %d' % self.port) try: self.server = ThreadingHTTPServer(('', self.port), RequestHandler) self.server.serve_forever() except Exception as e: log.error('http thread error: %s' % e) time.sleep(0.5) log.info('http thread: bye')
def run(self): """线程主函数 启动定时器,用于检查心跳超时 循环运行,接受新的客户端的连接。 """ log.info("http thread: start, port: %d" % self.port) try: self.server = ThreadingHTTPServer(("", self.port), RequestHandler) self.server.serve_forever() except Exception as e: log.error("http thread error: %s" % e) time.sleep(0.5) log.info("http thread: bye")
def main(self): log.info('main: start') # start rtk & loop self.rtk_mgr.start() # wait self.wait_for_keyboard() # quit & clean up self.rtk_mgr.running = False self.rtk_mgr.join() log.info('main: bye') log.close_all() base64_log.close_all()
def stop_thread(self, name): """停止某 rtk 线程,不等待 之后需要调用 wait_for_thread Args: name (str): rtk 线程名 """ try: if name in self.rtk_threads.keys(): rtk_thread = self.rtk_threads[name] if isinstance(rtk_thread, RtkGroup): rtk_thread.stop() log.info('main: require stop thread %d %s.' % (rtk_thread.thread_id, name)) except Exception as e: log.error('main: failed to stop thread %s: %s' % (name, e))
def run(self): """线程主函数 循环运行,接收来自客户端的数据并丢弃,向客户端发送 data_queue 中的数据包。 当 data_queue 过长时,丢弃旧的数据包。 """ log.info('station connection thread: start, %s' % self.address) self.update_status_cb(RtkStatus.S_CONNECTED) try: self.send_and_receive_data() except Exception as e: log.error('station connection thread error: %s' % e) self.update_status_cb(RtkStatus.S_DISCONNECTED) self.running = False log.info('station connection thread: bye')
def accept_client(self, server): """监听控制端口 尝试接受连接,如果有新连接,就关闭旧的连接。 Args: server (socket.socket): server socket """ try: conn, address = server.accept() self.disconnect_client() self.client = conn self.client.settimeout(3) self.is_transparency = False # 新连接不透传 log.info('new control client from: %s' % str(address)) except socket.timeout: pass
def update_names(names): """更新 rtk 服务名字列表 Args: names (list[str]): 开启的 rtk 服务名 """ log.info("http thread: load %d name(s)" % len(names)) # update status # add new name for name in names: if name not in RtkStatus.rtk_status: RtkStatus.rtk_status[name] = RtkStatus.S_UNKNOWN RtkStatus.rtk_last_rcv_time[name] = "NULL" # delete outdated name for name in RtkStatus.rtk_status.copy().keys(): if name not in names: del RtkStatus.rtk_status[name] del RtkStatus.rtk_last_rcv_time[name]
def update_names(names): """更新 rtk 服务名字列表 Args: names (list[str]): 开启的 rtk 服务名 """ log.info('http thread: load %d name(s)' % len(names)) # update status # add new name for name in names: if name not in RtkStatus.rtk_status: RtkStatus.rtk_status[name] = RtkStatus.S_UNKNOWN RtkStatus.rtk_last_rcv_time[name] = 'NULL' # delete outdated name for name in RtkStatus.rtk_status.copy().keys(): if name not in names: del RtkStatus.rtk_status[name] del RtkStatus.rtk_last_rcv_time[name]
def wait_for_thread(self, name): """等待某 rtk 线程完全退出 在 stop_thread 之后调用 Args: name (str): rtk 线程名 """ try: if name in self.rtk_threads.keys(): rtk_thread = self.rtk_threads[name] if isinstance(rtk_thread, RtkGroup): # wait rtk_thread.join() log.info('main: thread %d %s has stopped.' % (rtk_thread.thread_id, name)) # remove del self.rtk_threads[name] except Exception as e: log.error('main: error when wait for thread %s: %s' % (name, e))
def main(self): log.info('main: start') # start rtk self.start_threads_from_config() # wait self.wait_for_keyboard() # quit & clean up for name in self.rtk_threads.keys(): self.stop_thread(name) for name in sorted(self.rtk_threads.keys()): self.wait_for_thread(name) self.stop_and_wait_for_web_interface() # clear queue while not self.status_queue.empty(): self.status_queue.get(block=False) log.info('main: bye') log.close_all()
def send_and_receive_data(self): """循环发送、接收数据""" timeout_count = 0 while self.running: # 发送数据 self.send_data_from_queue() # 接收数据 try: data = self.client_socket.recv(BUFFER_SIZE) # 连接失败的处理 if len(data) == 0: raise RuntimeError('socket connection broken') # 收到数据后的处理 self.parse_data(data) timeout_count = 0 except socket.timeout: # 超时处理,超时 10 次时主动断开 # 超时时间短是为了在需要时能快速退出 timeout_count += 1 if timeout_count >= 10: self.running = False log.info('station connection thread: timeout') self.disconnect()
def check_new_connections(self): """检查新建的连接是否通过了握手测试""" new_connections = self.new_connections[:] time_sec = time.time() for i in range(0, len(new_connections)): connection_thread, established_time_sec = new_connections[i] # 逐个检查新建立的连接,判断是否握手成功、是否超时 if connection_thread.handshake_ok: # 握手成功 if self.connection_thread is not None and self.connection_thread.is_alive( ): # stop old log.info('stopping existing station connection thread.') self.connection_thread.got_data_cb = lambda data: None self.connection_thread.update_status_cb = lambda status: None self.connection_thread.running = False # 不用等待 # set new connection_thread self.connection_thread = connection_thread self.connection_thread.got_data_cb = self.got_data_cb self.connection_thread.update_status_cb = self.update_status_cb self.new_connections.remove( (connection_thread, established_time_sec)) elif (time_sec - established_time_sec > handshake_timeout_second) or time_sec < established_time_sec: # 超时 connection_thread.running = False # 不用等待 connection_thread.got_data_cb = lambda data: None connection_thread.update_status_cb = lambda status: None self.new_connections.remove( (connection_thread, established_time_sec)) if len(self.new_connections) == 0 and \ self.connection_thread is not None and not self.connection_thread.running: # 没有新连接,旧连接有过但也断了,那么判为断开状态 self.update_status_cb(RtkStatus.S_DISCONNECTED)
def run(self): """线程主函数 循环运行,建立连接、接收数据,并在连接出错时重连。 """ log.info('station client thread: start') while self.running: try: # 建立连接 conn = self.connect() log.info('station client thread: connected') # 开启数据接收线程 self.run_receive_data_thread(conn) except Exception as e: log.error('station client thread error: %s' % e) time.sleep(3) # disconnect self.disconnect() log.info('station client thread: bye')
def run(self): """线程主函数 循环运行,接收来自客户端的数据并丢弃,向客户端发送 data_queue 中的数据包。 当 data_queue 过长时,丢弃旧的数据包。 """ log.info('sender thread %d: start, %s' % (self.sender_id, self.address)) try: while self.running: # ignore old data while self.data_queue.qsize() > 10: self.data_queue.get(block=False) # send data try: data = self.data_queue.get(timeout=1) self.client_socket.settimeout(5) self.client_socket.sendall(data) self.send_count += 1 self.data_queue.task_done() except queue.Empty: pass # rcv useless data try: self.client_socket.settimeout(0.1) rcv_buf = self.client_socket.recv(256) if len(rcv_buf) == 0: log.info('sender thread %d has disconnected.' % self.sender_id) # 退出 break except socket.timeout: pass except Exception as e: log.error('sender thread %d error: %s' % (self.sender_id, e)) self.disconnect() self.running = False log.info('sender thread %d: bye' % self.sender_id)