在Windows上使用Python 2.5下载时出现urlopen错误10045,'地址已在使用中
我正在写一段代码,这段代码可以在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 个回答
所有迹象都表明,当前可用的网络连接(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有点生疏,所以如果我漏掉了什么也不奇怪。
换个角度想,你似乎想解决的问题其实已经有一个叫做rsync的程序解决了。你可以找找看有没有Windows版本,看看它是否能满足你的需求。
如果真的是资源问题(释放操作系统的套接字资源)
可以试试这个:
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 监控我的进程和网络。