5

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

服务器端是基于 80 端口的 Ubuntu 上的普通 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(有些可能已过期并被重用),它开始看起来更像链接文章描述的真正问题。但是,我仍然不确定如何解决这个问题。进行注册表编辑不是一种选择。

4

5 回答 5

5

如果真的是资源问题(释放os socket资源)

试试这个:

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 次下载)

我用进程黑客监视我的进程和网络。

于 2009-10-08T13:15:28.930 回答
1

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

于 2009-10-02T23:44:41.090 回答
1

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

于 2009-10-09T01:34:51.383 回答
1

而不是为每个请求打开一个新的 TCP 连接,您应该真正使用持久的 HTTP 连接 - 看看urlgrabber(或者,只是在keepalive.py中了解如何向 urllib2 添加保持活动连接支持)。

于 2009-10-11T17:03:06.963 回答
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()

对不起,我的蟒蛇有点生锈,所以如果我错过了什么,我不会感到惊讶。

于 2009-10-11T18:11:15.687 回答