在Windows上的Python 2.5中下载时,urlopen错误10045,'地址已在使用'

时间:2009-10-02 22:15:25

标签: python windows http download urllib2

我正在编写将在Linux,OS X和Windows上运行的代码。它从服务器下载大约55,000个文件的列表,然后逐步检查文件列表,检查文件是否存在于本地。 (使用SHA哈希验证和其他一些好东西。)如果文件不在本地存在或哈希不匹配,则下载它们。

服务器端在Ubuntu上通过端口80是普通的Apache 2。

客户端在Mac和Linux上运行良好,但在下载了大量文件后,在Windows(XP和Vista)上给出了这个错误:

urllib2.URLError: <urlopen error <10048, 'Address already in use'>>

此链接:http://bytes.com/topic/python/answers/530949-client-side-tcp-socket-receiving-address-already-use-upon-connect指向TCP端口耗尽,但“netstat -n”从未向我显示超过六个处于“TIME_WAIT”状态的连接,即使只是在它出错之前。

代码(为其下载的55,000个文件分别调用一次)是:

request = urllib2.Request(file_remote_path)
opener = urllib2.build_opener()
datastream = opener.open(request)
outfileobj = open(temp_file_path, 'wb')
try:
    while True:
        chunk = datastream.read(CHUNK_SIZE)
        if chunk == '':
            break
        else:
            outfileobj.write(chunk)
finally:
    outfileobj = outfileobj.close()
    datastream.close()

更新:我通过greping日志发现它正好进入下载例程3998次。我已经多次运行它,每次都失败了3998。鉴于链接文章指出可用端口是5000-1025 = 3975(有些可能已到期并被重用),它开始看起来更像链接文章描述真正的问题。但是,我仍然不确定如何解决这个问题。进行注册表编辑不是一种选择。

5 个答案:

答案 0 :(得分:5)

如果它真的是资源问题(释放os套接字资源)

试试这个:

request = urllib2.Request(file_remote_path)
opener = urllib2.build_opener()

retry = 3 # 3 tries
while retry :
    try :
        datastream = opener.open(request)
    except urllib2.URLError, ue:
        if ue.reason.find('10048') > -1 :
            if retry :
                retry -= 1
            else :
                raise urllib2.URLError("Address already in use / retries exhausted")
        else :
            retry = 0
    if datastream :
        retry = 0

outfileobj = open(temp_file_path, 'wb')
try:
    while True:
        chunk = datastream.read(CHUNK_SIZE)
        if chunk == '':
            break
        else:
            outfileobj.write(chunk)
finally:
    outfileobj = outfileobj.close()
    datastream.close()

如果你想要你可以插入一个睡眠,或者你可以依赖它

在我的win-xp上,问题没有显示(我下载了5000次)

我使用process hacker观看我的流程和网络。

答案 1 :(得分:1)

在盒子外思考,你似乎试图解决的问题已经被一个名为rsync的程序解决了。您可能会寻找Windows实现,看看它是否满足您的需求。

答案 2 :(得分:1)

您应该认真考虑复制和修改this pyCurl example,以便有效下载大量文件。

答案 3 :(得分:1)

不应为每个请求打开一个新的TCP连接,而应该使用持久的HTTP连接 - 请查看urlgrabber(或者只是keepalive.py,了解如何添加keep-alive连接支持到urllib2)。

答案 4 :(得分:1)

所有迹象都表明缺少可用的插座。你确定只有6个处于TIME_WAIT状态吗?如果您正在运行如此多的下载操作,netstat很可能会超出您的终端缓冲区。我发现netstat stat在正常使用期间超出了我的终端。

解决方案是修改代码以重用套接字。或者引入超时。跟踪你有多少个开放插座也没什么坏处。优化等待。 Windows XP的默认超时为120秒。如果你的插座耗尽,你至少要睡这么长时间。不幸的是,当套接字关闭并离开TIME_WAIT状态时,看起来不容易从Python检查。

鉴于请求和超时的异步性质,执行此操作的最佳方法可能是在一个线程中。使每个威胁在完成之前睡眠2分钟。您可以使用信号量或限制活动线程数,以确保不会耗尽套接字。

这是我如何处理它。您可能希望将异常子句添加到fetch部分的内部try块,以警告您有关提取失败的信息。

import time
import threading
import Queue

# assumes url_queue is a Queue object populated with tuples in the form of(url_to_fetch, temp_file)
# also assumes that TotalUrls is the size of the queue before any threads are started.


class urlfetcher(threading.Thread)
    def __init__ (self, queue)
        Thread.__init__(self)
        self.queue = queue


    def run(self)
        try: # needed to handle empty exception raised by an empty queue.
            file_remote_path, temp_file_path = self.queue.get()
            request = urllib2.Request(file_remote_path)
            opener = urllib2.build_opener()
            datastream = opener.open(request)
            outfileobj = open(temp_file_path, 'wb')
            try:
                while True:
                    chunk = datastream.read(CHUNK_SIZE)
                    if chunk == '':
                        break
                    else:
                        outfileobj.write(chunk)
            finally:
                outfileobj = outfileobj.close()
                datastream.close()    
                time.sleep(120)
                self.queue.task_done()

elsewhere:


while url_queue.size() < TotalUrls: # hard limit of available ports.
    if threading.active_threads() < 3975: # Hard limit of available ports
         t = urlFetcher(url_queue)
         t.start()
    else: 
        time.sleep(2)

url_queue.join()

抱歉,我的python有点生疏,所以如果我错过了什么,我不会感到惊讶。