我正在编写将在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(有些可能已到期并被重用),它开始看起来更像链接文章描述真正的问题。但是,我仍然不确定如何解决这个问题。进行注册表编辑不是一种选择。
答案 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有点生疏,所以如果我错过了什么,我不会感到惊讶。