python socket recv() 和信号
Posted
技术标签:
【中文标题】python socket recv() 和信号【英文标题】:python socket recv() and signals 【发布时间】:2013-04-18 23:11:42 【问题描述】:我有一个简单的(非线程)脚本,它在套接字上侦听数据,对其进行分析并在内部使用 SIGALRM
's 在预定义的计时器内部发送电子邮件。
问题是在recv()
循环期间,SIGALRM
的出现似乎引发了一个
socket.error: [Errno 4] Interrupted system call
并因此终止程序。
我可以用 try/except 块包装recv()
,但我想知道在此期间我是否会丢失任何数据,或者缓冲区是否会防止丢失。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
while True:
try:
data = s.recv(2048)
except socket.error, e:
pass
yield data
s.close()
return
【问题讨论】:
【参考方案1】:在 C 中处理这个问题的标准方法是循环 EINTR
。而且,虽然 不应该在 Python 中是必需的,但它确实是。
您的代码非常接近处理此问题的惯用方式,除了两件事:
您不想忽略所有个错误,只需EINTR
。
你不能yield data
这样忽略错误,因为你会重新产生前一个数据包(如果有的话)或引发NameError
(如果这是第一次通过循环) .
所以:
while True:
try:
data = s.recv(2048)
except socket.error, e:
if e.errno != errno.EINTR:
raise
else:
yield data
那么,你为什么要这样做呢?
POSIX 几乎允许任何系统调用针对某些类型的临时故障(包括被信号中断)返回 EINTR。许多 POSIX 平台都这样做。预期的应用程序行为是重试(如果您正在尝试阻塞调用)或返回循环(如果您在电平触发反应器内)。 This blog post 很好地解释了为什么 POSIX 以这种方式工作。 (这是事后的理由,绝对不是真正的理由……)另见the glibc documentation。
与大多数脚本语言一样,Python 应该在内部包装所有 EINTR
-prone 调用,因此您不必考虑这一点(除非您使用第三方 C 扩展)。但不幸的是,它有错误。发现并修复的最新案例集位于issue 9867 和issue 12268。
即使他们最终掌握了一切,也只有在您能够依赖足够新的 Python 版本时才会有所帮助。鉴于您使用的是 2.6 之前样式的 except
语法,并且最新修复进入了一些 2.7.x 和 3.2.x 错误修复版本,这可能对您不起作用。
还有其他方法可以解决此问题,但它们更复杂且更不便携。例如,您可以将阻塞 recv
替换为阻塞 pselect
和非阻塞 recv
,将 pipe
与套接字一起添加到 fd 集中,将所有信号处理程序替换为仅向该管道写入(一个字节),并将实际的信号处理代码移动到事件循环中。然后,在某些平台上,您永远不会收到EINTR
。但这可能不是您希望在 Python 中采用的方法。
【讨论】:
以上是关于python socket recv() 和信号的主要内容,如果未能解决你的问题,请参考以下文章