def recv_data(cls, net_info, recv_para={}): """ 从指定的网络连接中读取数据 @param {object} net_info - 要读取数据的网络信息对象(例如socket对象) @param {dict} recv_para - 读取数据的参数, 包括: recv_len {int} - 要获取的数据长度, 必要参数 overtime {int} - 获取超时时间,单位为毫秒,非必要参数 @returns {CResult} - 数据获取结果: result.code :'00000'-成功,'20403'-获取数据超时,其他为获取失败 result.data :获取到的数据对象(具体类型和定义,由实现类自定义) result.recv_time : datetime 实际开始接受数据时间 result.overtime : int 超时时间(毫秒),当返回结果为超时,可获取超时时间信息 """ # 子类必须定义该功能 if type(recv_para) != dict: recv_para = {} _result = CResult('00000') _result.data = b'' _result.recv_time = datetime.datetime.now() _overtime = 10000 if 'overtime' in recv_para.keys(): # 外部有传入,优先使用该超时时间 _overtime = recv_para['overtime'] elif hasattr(net_info, 'recv_timeout'): # 如果net_info有超时的设置 _overtime = net_info.recv_timeout _result.overtime = _overtime with ExceptionTool.ignored_cresult( _result ): _rest_bytes = recv_para['recv_len'] while _rest_bytes > 0: # 检查是否超时 if (datetime.datetime.now() - _result.recv_time).total_seconds() * 1000 > _overtime: # 已超时 _result.change_code(code='20403') break _buffer = b'' with ExceptionTool.ignored(expect=(BlockingIOError)): # 获取数据 _buffer = net_info.csocket.recv(_rest_bytes) if len(_buffer) > 0: _result.data = _result.data + _buffer _rest_bytes = _rest_bytes - len(_buffer) else: # 休眠一下 RunTool.sleep(0.001) return _result
def send_data(cls, net_info, data, send_para={}): """ 向指定的网络连接发送数据 @param {object} net_info - 要写入数据的网络信息对象(例如socket对象) @param {object} data - 要写入的数据对象(具体类型和定义,由实现类自定义) @param {dict} send_para - 写入数据的参数: overtime {int} - 发送超时时间,单位为毫秒, 非必须参数 @returns {CResult} - 发送结果: result.code :'00000'-成功,'20404'-写入数据超时,其他为写入失败 result.send_time : datetime 实际发送完成时间 result.overtime : int 超时时间(毫秒),当返回结果为超时,可获取超时时间信息 """ # 子类必须定义该功能 if type(send_para) != dict: send_para = {} _result = CResult('00000') _result.send_time = None _overtime = 10000 if 'overtime' in send_para.keys(): _overtime = send_para['overtime'] elif hasattr(net_info, 'send_timeout'): # 如果net_info有超时的设置 _overtime = net_info.send_timeout _result.overtime = _overtime _begin_time = datetime.datetime.now() with ExceptionTool.ignored_cresult( _result ): _rest_bytes = len(data) _total_bytes = _rest_bytes while _rest_bytes > 0: # 检查是否超时 if (datetime.datetime.now() - _begin_time).total_seconds() * 1000 > _overtime: # 已超时 _result.change_code(code='20404') break with ExceptionTool.ignored(expect=(BlockingIOError)): # 发送数据 _len = net_info.csocket.send(data[_total_bytes - _rest_bytes:]) if _len > 0: _rest_bytes = _rest_bytes - _len _result.send_time = datetime.datetime.now() return _result
def ignored_cresult(result_obj=None, error_map={}, expect=(), expect_no_log=False, expect_use_error_map=True, logger=None, self_log_msg='', force_log_level=None, i18n_obj=None, i18n_msg_paras=(), debug=False): """ 忽略异常并设置CResult对象,简化异常捕获代码,利用该函数忽略指定的异常,并设置传入的通用结果对象,详细说明如下: 1、对于指定忽略的异常,忽略不处理,结果为成功(如果指定logger则会进行日志输出,使用WARNING级别) 2、对于非指定的异常,不抛出异常,结果为失败(如果指定logger则会进行日志输出,使用ERROR级别) 3、输出的日志为self_log_msg+'\n'+trace_str 4、根据error_map的映射关系设置错误码和错误信息 @param {CResult} result_obj=None - 需要设置的错误类对象(对象值会被修改) @param {dict} error_map={} - 用来设置错误类对象的映射表,具体说明如下: 1、key为异常类,value为(code, msg)的错误码、错误描述二元组,如果msg=None代表使用标准错误码 2、应有一个'DEFAULT'的key,代表没有匹配上的异常映射,默认value为('29999', None) 3、应有一个'SUCESS'的key,代表成功的映射,默认value为('00000', None) 注:value也可以为(code, msg, i18n_msg_paras)的错误码、错误描述、国际化替换参数三元组, i18n_msg_paras为tuple类型, 使用该模式支持CResult的国际化处理 @param {tuple} expect=() - 需要忽略的异常列表,例如(ZeroDivisionError, ValueError) @param {bool} expect_no_log=False - 忽略异常列表是否不打印日志 @param {bool} expect_use_error_map=True - 忽略异常列表所匹配到的异常,所返回错误码是否使用错误码映射表: 如果在映射表中匹配上则返回映射表的错误码;匹配不上则返回成功 @param {object} logger=None - 日志对象,如果为None代表不需要输出日志,传入对象需满足: 1、标准logging的logger对象 2、自定义的日志类对象,但应实现warning、error的标准方法 @param {string} self_log_msg='' - 需要输出的自定义日志信息 @param {int} force_log_level=None - 强制遇到所有异常统一按指定的日志级别输出(logging.INFO/...) @param {object} i18n_obj=None - 国际化类的实例对象,该对象需实现translate方法 @param {tuple} i18n_msg_paras=() - 与self_log_msg配套使用,当使用国际化时,可以传入变量,用于替换self_log_msg中的$1占位符 @param {bool} - debug=False - 是否调试模式,如果是调试模式,当没有logger时使用print输出堆栈信息 @example result = CResult() with ExceptionTools.ignored_CResult(result_obj=result, error_map={},expect=(),logger=None,self_log_msg=''): i = 1/0 i = i + 1000 print(str(result)) """ _error_map = copy.deepcopy(error_map) try: # 初始化对象 if result_obj is None: result_obj = CResult(code='00000', msg=None, i18n_obj=i18n_obj) # 确保映射表中有默认值 if 'SUCESS' not in _error_map.keys(): _error_map['SUCESS'] = ('00000', None, i18n_msg_paras) if 'DEFAULT' not in _error_map.keys(): _error_map['DEFAULT'] = ('29999', None, i18n_msg_paras) # 执行with对应的脚本 yield except expect as ex: # 匹配到指定异常,输出日志 if not expect_no_log: _log_level = logging.WARNING if force_log_level is not None: _log_level = force_log_level _self_log_msg = '' if i18n_obj is not None: _self_log_msg = i18n_obj.translate( self_log_msg, replace_para=i18n_msg_paras) else: _self_log_msg = _(self_log_msg, *i18n_msg_paras) ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(ex)), _self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) # 按成功处理 _error = sys.exc_info() _trace_str = traceback.format_exc() if expect_use_error_map and _error[0] in _error_map.keys(): if len(_error_map[_error[0]]) < 3: result_obj.change_code(code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1]) else: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1], i18n_msg_paras=_error_map[_error[0]][2]) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str else: # 按成功处理 pass except Exception as e: # 其他异常,输出日志,获取失败信息 _error = sys.exc_info() _trace_str = traceback.format_exc() if _error[0] in _error_map.keys(): if len(_error_map[_error[0]]) < 3: result_obj.change_code(code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1]) else: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1], i18n_msg_paras=_error_map[_error[0]][2]) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str else: # 其他失败 if len(_error_map['DEFAULT']) < 3: result_obj.change_code(code=_error_map['DEFAULT'][0], msg=_error_map['DEFAULT'][1]) else: result_obj.change_code( code=_error_map['DEFAULT'][0], msg=_error_map['DEFAULT'][1], i18n_msg_paras=_error_map['DEFAULT'][2]) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str _log_level = logging.ERROR if force_log_level is not None: _log_level = force_log_level _self_log_msg = '' if i18n_obj is not None: _self_log_msg = i18n_obj.translate(self_log_msg, replace_para=i18n_msg_paras) else: _self_log_msg = _(self_log_msg, *i18n_msg_paras) ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(e)), _self_log_msg), trace_str=result_obj.trace_str, log_level=_log_level, debug=debug)
def recv_http_head(cls, net_info, recv_para={}): """ 获取http报文头对象 @param {object} net_info - 要读取数据的网络信息对象 @param {dict} recv_para - 获取参数,暂未使用 @returns {CResult} - 数据获取结果: result.code :'00000'-成功,'20403'-获取数据超时,其他为获取失败 result.data :获取到的数据对象,类型为MsgHTTP result.recv_time : datetime 实际开始接受数据时间 """ if type(recv_para) != dict: recv_para = {} _result = CResult('00000') _result.data = None _result.recv_time = datetime.datetime.now() _overtime = 10000 if 'overtime' in recv_para.keys(): # 外部有传入,优先使用该超时时间 _overtime = recv_para['overtime'] elif hasattr(net_info, 'recv_timeout'): # 如果net_info有超时的设置 _overtime = net_info.recv_timeout _result.overtime = _overtime _recv_para = copy.deepcopy(recv_para) _recv_para['recv_len'] = 1 with ExceptionTool.ignored_cresult( _result ): # 循环获取所有报文头内容 _get_line_bytes = b'' _last_is_ln = False # 标记上一个字符是否回车换行 while True: # 检查是否超时 if (datetime.datetime.now() - _result.recv_time).total_seconds() * 1000 > _overtime: # 已超时 _result.change_code(code='20403') break _read_result = TcpIpService.recv_data(net_info, _recv_para) if not _read_result.is_success(): # 出现异常,直接返回失败 _read_result.data = None _read_result.recv_time = _result.recv_time return _read_result # 获取成功,判断是否回车换行 _get_line_bytes = _get_line_bytes + _read_result.data if str(_read_result.data, "ascii") == "\n" and _last_is_ln: # 已经连续遇到两次回车换行,说明报文头已收齐退出循环 break elif str(_read_result.data, "ascii") == "\n": _last_is_ln = True continue elif str(_read_result.data, "ascii") == "\r": continue else: # 其他字符,不是连续换行 _last_is_ln = False continue # 取完报文头数据,转换为结构对象 _result.data = MsgHTTP(_get_line_bytes, msg_id=None, obj_type=EnumMsgObjType.Bytes) return _result