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

5 投票
5 回答
1418 浏览
提问于 2025-04-15 14:45

我正在写一段代码,这段代码可以在Linux、OS X和Windows上运行。它会从服务器下载大约55,000个文件的列表,然后逐个检查这些文件在本地是否存在。(还会进行SHA哈希验证和其他一些小功能。)如果文件在本地不存在或者哈希不匹配,它就会下载这些文件。

服务器端使用的是普通的Apache 2,运行在Ubuntu上,使用的是80端口。

客户端在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”状态,即使在出错之前也是如此。

这段代码(每下载一个文件就会调用一次)是:

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()

更新:我通过查看日志发现,它确实进入下载程序的次数正好是3998次。我多次运行这个程序,每次在3998的时候都会失败。根据链接的文章,可用的端口是5000-1025=3975(而且有些可能正在过期并被重用),这看起来越来越像是链接文章描述的真正问题。不过,我仍然不确定该如何解决这个问题。修改注册表不是一个选项。

5 个回答

1

所有迹象都表明,当前可用的网络连接(socket)不够。你确定只有6个处于TIME_WAIT状态吗?如果你正在进行很多下载操作,很可能netstat的输出超出了你的终端显示能力。我发现,在正常使用期间,netstat的输出经常会让我的终端显示不全。

解决办法是修改代码,让连接可以重复使用,或者设置一个超时时间。记录一下你打开了多少个连接也是个好主意,这样可以优化等待时间。在Windows XP上,默认的超时时间是120秒,所以如果你用完了连接,最好等至少这么久。不幸的是,从Python中检查一个连接何时关闭并离开TIME_WAIT状态似乎没有简单的方法。

考虑到请求和超时是异步的,最好的办法可能是在一个线程中处理。让每个线程在完成之前睡2分钟。你可以使用信号量(Semaphore)或者限制活动线程的数量,以确保不会用完连接。

这是我处理这个问题的方法。你可能想在获取数据的内部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有点生疏,所以如果我漏掉了什么也不奇怪。

1

换个角度想,你似乎想解决的问题其实已经有一个叫做rsync的程序解决了。你可以找找看有没有Windows版本,看看它是否能满足你的需求。

5

如果真的是资源问题(释放操作系统的套接字资源)

可以试试这个:

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()

如果你想的话,可以插入一个暂停,或者让它依赖于操作系统

在我的Windows XP上,这个问题没有出现(我下载了5000次)

我用 Process Hacker 监控我的进程和网络。

撰写回答