我正在尝试使用TCPServer处理难以复制的错误案例。我们已经看到一些事件,当处理程序中发生套接字超时时,代码永远不会恢复并继续返回socket.timeout异常。 这看起来来自Socket.py库代码中的以下片段:
def readinto(self, b):
"""Read up to len(b) bytes into the writable buffer *b* and return
the number of bytes read. If the socket is non-blocking and no bytes
are available, None is returned.
If *b* is non-empty, a 0 return value indicates that the connection
was shutdown at the other end.
"""
self._checkClosed()
self._checkReadable()
if self._timeout_occurred:
raise OSError("cannot read from timed out object")
while True:
try:
return self._sock.recv_into(b)
except timeout:
self._timeout_occurred = True
raise
except InterruptedError:
continue
except error as e:
if e.args[0] in _blocking_errnos:
return None
raise
一旦发生超时_timeout_occurred设置为True,并且下一次进入此函数,套接字已设置标志,并将立即退出,无法从超时对象中读取错误。
现在代码使用TCP Server(仅包含相关代码)基本上它是从套接字中读取内容,并将其排队以便单独处理。
def get_event(file_):
pre_package_len = 8
msg = file_.read(pre_package_len)
if len(msg) == pre_package_len:
pkg = PRE_PACKAGE_FRAME.unpack(msg)
msg = file_.read(pkg['len'])
logger.debug('recv: type: %s len: %s bytes read: %s',
pkg['type'], pkg['len'], len(msg))
if len(msg) >= pkg['len']:
if pkg['type'] == cdefs.kNotification:
e = EVENT_FRAME.unpack(msg)
return decode_event(e)
logger.warn('received unsupported package type: %s', pkg['type'])
else:
logger.error('failed to recv')
class _EventHandler(StreamRequestHandler):
def handle(self):
logger.debug("Got event from %s", self.client_address)
try:
e = get_event(self.rfile)
if e:
self.q.put(e)
except socket.timeout:
logger.error('timed out reading event')
def process_event(q, handler, shutdown_sentinel):
for e in iter(q.get, shutdown_sentinel):
try:
handler(e)
except Exception:
logger.exception('Unhandled exception handling event: %s', e)
logger.info('exiting')
def eventhandler_maker(q, timeout):
return type('NewEventHandler',
(_EventHandler, object),
dict(q=q, timeout=timeout))
def process_events(handler, address, timeout=20):
sentinel = object()
q = Queue()
eventhandler = eventhandler_maker(q, timeout)
server = TCPServer(address, eventhandler)
start_thread(server.serve_forever)
start_thread(process_event, (q, handler, sentinel))
def shutdown():
logger.info('shutting down')
q.put(sentinel)
server.shutdown()
def add_event(e):
q.put(e)
return shutdown, add_event
症状是一旦超时发生,日志会一直显示“超时读取事件”,代码再也不会执行任何操作了。我添加了代码来转储server.socket.gettimeout()和socket.getdefaulttimeout(),并且都返回None。该应用程序在带有python 3.4.0的嵌入式Linux 3.10内核上运行。
我在这里有两个问题: