2009-10-02 26 views
5

我正在编写将在Linux,OS X和Windows上运行的代码。它从服务器下载大约55,000个文件的列表,然后遍历文件列表,检查文件是否存在于本地。 (使用SHA哈希验证和其他一些好处。)如果文件不在本地或哈希不匹配,它会下载它们。urlopen错误10045,在Windows上在Python 2.5下载时'地址已被使用'

服务器端是纯香草的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”状态,甚至只是之前它出错了。

代码(每个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

如果真的是一个资源问题(释放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() 
,如果你愿意,你可以插入一个睡眠或你让它OS依赖

我的win-xp问题没有出现(我达到了5000次下载)

我看我的流程和网络与process hacker

+0

感谢您处理黑客的链接 – Natascha 2009-10-14 12:45:16

1

在盒子外面思考,你似乎试图解决的问题已经被称为rsync的程序解决了。您可能会寻找Windows实施并查看它是否符合您的需求。

+0

由于各种原因,使用Python编写我们自己的Python版本是我们的最佳选择。 rsync的“杀手功能”是部分下载 - 只发送不同文件的部分。我们没有重复那个功能;这只是简单地列出一个列表并下载完整的文件。我们正在重塑一个非常平凡的车轮。 – Schof 2009-10-03 00:33:33

+0

值得一提的是,'rsync -W'复制整个文件 – 2009-10-11 21:08:56

+0

如果没有下载整个文件并生成校验和或在远程系统上运行代码以生成一个远程系统文件一个校验和,然后下载如果结果已经改变。我同意rsync或类似的工具(windows上的deltacopy,实现rsync的一些库,纯python实现,无论什么)都是你最好的选择。 – 2009-10-14 10:40:02

1

而不是打开每个请求的新的TCP连接,你应该使用持久的HTTP连接 - 看看urlgrabber(或者,在keepalive.py如何添加保持连接支持urllib2)。

1

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

解决方法是修改代码以重用套接字。或者引入超时。跟踪您拥有多少个开放式套接字也不会有什么坏处。优化等待。 Windows XP的默认超时时间为120秒。所以如果你用完了插座,你至少要睡上这么长时间。不幸的是,当套接字关闭并且离开TIME_WAIT状态时,看起来并不像Python那样容易。

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

下面是我如何处理它。您可能需要将一个异常子句添加到获取部分的内部尝试块中,以警告您失败的提取。

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

对不起,我的蟒蛇有点生疏,所以如果我错过了某些东西,我不会感到惊讶。