Python 2.6 urlib2超时问题

2024-05-15 11:28:36 发布

您现在位置:Python中文网/ 问答频道 /正文

似乎我无法考虑urllib2超时。 我确实读了——我想——所有与这个主题相关的帖子,看来我没有做错什么。我说的对吗? 非常感谢你的帮助。在

场景:

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

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

一些信息:

  • Ubuntu10.04.4 LTS(运行在VirtualBox v4.2.6虚拟机上,主机操作系统是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连接器插入

^{pr2}$

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

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

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

添加到原始post:test results(使用IP而不是FQDN)

正如@unutbu(见注释)所建议的那样,用一个IP地址替换myhost中的FQDN可以解决问题:超时生效。在

LAN连接器已插入…
$time/网络访问 [---Net_Access()---互联网接入正常

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

LAN连接器拔出…
$time/网络访问 [---Net_Access()---无网络访问

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

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

是否有人找到了一种方法来使用urllib2超时而不进入pre-DNS解析并将IP传递给函数,还是您首先使用socket测试连接,然后在确定可以到达目标时激发urllib2?在

非常感谢。在


Tags: ip网络netaccesstimesystimeoutsocket
2条回答

或许可以试试这个:

import urllib2

def get_header(url):
    req = urllib2.Request(url)
    req.get_method = lambda : 'HEAD'
    try:
        response = urllib2.urlopen(req)
    except urllib2.URLError:
        # urllib2.URLError: <urlopen error [Errno -2] Name or service not known>
        return False
    return True

url = 'http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.7.1.tar.bz2'
print(get_header(url))

当我拔掉网络适配器的插头时,它几乎立即输出False,而在正常情况下,它会输出True。在

我不知道为什么与您的原始代码相比,它的工作速度如此之快(甚至不需要设置timeout参数),但也许它也适用于您。在


今天早上我做了一个实验,结果导致get_header不能立即返回。我在路由器关闭的情况下启动了计算机。然后路由器被打开了。然后通过Ubuntu GUI启用了网络和无线。无法建立工作连接。在此阶段,get_header无法立即返回。在

因此,这里有一个更重的解决方案,它在使用multiprocessing.Pool的子进程中调用get_headerpool.apply_async返回的对象有一个带超时参数的get方法。如果在timeout指定的持续时间内没有从get_header返回结果,则子进程将终止。在

因此,check_http在任何情况下都应该在大约1秒内返回结果。在

^{pr2}$

如果您的问题是DNS查找在没有网络连接的情况下要花很长时间(或太长时间)超时,那么是的,这是一个已知的问题,您在urllib2本身内无法解决该问题。在

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

首先,让我们看看发生了什么。最终,urlopen依赖于getaddrinfo,它(以及它的亲属,如gethostbyname)是著名的套接字API中的一个关键部分,不能异步运行或中断(在某些平台上,它甚至不是线程安全的)。如果您想自己跟踪源代码,^{}将遵从^{}来创建连接,后者调用^{}上的create_connection,后者调用^{}上的socket_getaddrinfo,后者最终调用真正的getaddrinfo函数。这是一个臭名昭著的问题,它影响着世界上每种语言编写的每个网络客户端或服务器,而且没有一个好的、简单的解决方案。在

一个选择是使用一个已经解决了这个问题的不同的高级库。我相信^{}依赖于^{},这最终也有相同的问题,但是^{}依赖于{a8},如果使用{a9}构建,它将异步执行名称查找,因此可以超时。在

当然,也可以使用twisted或{}或其他异步网络库。但是显然,重写所有代码以使用twistedHTTP客户机而不是{}并不是很简单。在

另一个选择是通过monkey修补标准库来“修复”urllib2。如果你想这样做,有两个步骤。在

首先,必须提供一个可超时的getaddrinfo。您可以通过绑定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甚至{}中的代码。在

最后一个选择是在更高的层次上解决这个问题。如果你的网络事件发生在后台线程上,你可以对整个事件抛出一个更高级别的超时,如果花了超过timeout秒的时间来判断它是否超时,你知道它已经超时了。在

相关问题 更多 >