Python 2.6 urlib2 超时问题

标签 python timeout urllib2

我似乎无法将 urllib2 超时考虑在内。 我确实阅读了 - 我想 - 所有与该主题相关的帖子,看来我没有做错任何事。我对么? 非常感谢您的帮助。

场景:

在继续脚本的其余部分之前,我需要检查 Internet 连接。 然后我编写了一个函数 (Net_Access),如下所示。

  • 当我在连接了 LAN 或 Wifi 接口(interface)的情况下执行此代码并检查现有主机名时:一切正常,因为没有错误或问题,因此没有超时。
  • 如果我拔下 LAN 连接器或检查不存在的主机名,超时值似乎会被忽略。 请问我的代码有什么问题吗?

一些信息:

  • Ubuntu 10.04.4 LTS(运行到 VirtualBox v4.2.6 VM,主机操作系统是 MAC OS X Lion)
  • cat/proc/sys/kernel/osrelease: 2.6.32-42-generic
  • python 2.6.5

我的代码:

#!/usr/bin/env python

import socket
import urllib2

myhost = 'http://www.google.com'
timeout = 3

socket.setdefaulttimeout(timeout)
req = urllib2.Request(myhost)

try:
    handle = urllib2.urlopen(req, timeout = timeout)
except urllib2.URLError as e:
    socket.setdefaulttimeout(None)
    print ('[--- Net_Access() --- No network access')
else:
    print ('[--- Net_Access() --- Internet Access OK')

1) 工作,插入 LAN 连接器

$ $ time ./Net_Access 
[--- Net_Access() --- Internet Access OK

real    0m0.223s
user    0m0.060s
sys 0m0.032s

2) 超时不工作,拔下 LAN 连接器

$ time ./Net_Access 
[--- Net_Access() --- No network access

real    1m20.235s
user    0m0.048s
sys 0m0.060s

添加到原始帖子:测试结果(使用 IP 而不是 FQDN)

正如@unutbu 所建议的(见评论),用 IP 地址替换 myhost 中的 FQDN 解决了​​这个问题:超时生效。

LAN 连接器已插入...
$时间./Net_Access [--- Net_Access() --- 上网正常

real    0m0.289s
user    0m0.036s
sys 0m0.040s

LAN 连接器已拔出...
$时间./Net_Access [--- Net_Access() --- 无网络访问权限

real    0m3.082s
user    0m0.052s
sys 0m0.024s

这很好,但这意味着超时只能用于 IP 而不是 FQDN。奇怪……

是否有人找到了一种方法来使用 urllib2 超时而不进入预 DNS 解析并将 IP 传递给该函数,或者您是先使用套接字测试连接然后在确定可以到达目标时触发 urllib2?

非常感谢。

最佳答案

如果您的问题是在没有网络连接时 DNS 查找永远(或太长时间)超时,那么是的,这是一个已知问题,您在 urllib2 内无能为力。自己来解决这个问题。

那么,所有的希望都破灭了吗?好吧,不一定。

首先,让我们看看发生了什么。最终,urlopen依赖getaddrinfo ,它(连同它的亲戚,如 gethostbyname )是众所周知的套接字 API 的一个关键部分,它不能异步运行或中断(在某些平台上,它甚至不是线程安全的)。如果您想自己追根溯源, urllib2 遵从 httplib 用于创建连接,调用 create_connection socket ,它调用 socket_getaddrinfo _socket ,最终调用真正的 getaddrinfo功能。这是一个臭名昭著的问题,它影响到世界上用每一种语言编写的每个网络客户端或服务器,并且没有好的、简单的解决方案。

一个选择是使用已经解决了这个问题的不同的高级库。我相信 requests 依赖 urllib3 最终有同样的问题,但是 pycurl 依赖 libcurl , 如果使用 c-ares 构建, 进行异步名称查找,因此可能会超时。

或者,当然,您可以使用类似 twisted 的东西或 tornado或其他一些异步网络库。但显然重写所有代码以使用 twisted HTTP 客户端而不是 urllib2并不是微不足道的。

另一种选择是“修复”urllib2通过 monkeypatching 标准库。如果您想这样做,有两个步骤。

首先,您必须提供一个可超时的 getaddrinfo .您可以通过绑定(bind) c-ares 来做到这一点,或使用 ctypes访问特定于平台的 API,如 linux 的 getaddrinfo_a ,甚至查找名称服务器并直接与它们通信。但真正简单的方法是使用线程。如果你正在做很多这样的事情,你会想要使用单个线程或小线程池,但对于小规模使用,只需为每次调用分离一个线程。一个真正快速而肮脏(阅读:糟糕)的实现是:

def getaddrinfo_async(*args):
    result = None
    t = threading.Thread(target=lambda: result=socket.getaddrinfo(*args))
    t.start()
    t.join(timeout)
    if t.isAlive():
        raise TimeoutError(blahblahblah)
    return result

接下来,您必须获取所有您关心的库才能使用它。根据您希望补丁的普及程度(和危险程度),您可以替换 socket.getaddrinfo本身,或者只是 socket.create_connection ,或者只是 httplib 中的代码甚至 urllib2 .

最后一个选择是在更高级别修复此问题。如果你的网络事情发生在后台线程上,你可以在整个事情上抛出一个更高级别的超时,如果它花费的时间超过 timeout秒来确定它是否超时,你知道它已经超时。

关于Python 2.6 urlib2 超时问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14127115/

相关文章:

python - 在 fileinput 模块中结合就地过滤和编码设置

python - 读取目录中的所有图像并另存为(.mat 文件矩阵)到另一个目录中

android - 为什么 android HttpURLConnection 不遵守超时值?

android - 什么是平均。移动应用程序的最佳连接超时?

python - BeautifulSoup 使用 POST 表单操作解析 html 时遇到问题

python - 将文件输入字段中的 InMemoryUploadedFile 发布到另一个外部服务器

python - 保存到 Django 中的模型后返回主键

Python客户端服务器通信

PHP: "The website has too many redirects"当使用 php session 时

Python 无法使用 urllib 或 Mechanize 检索表单