2009-10-02 38 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() 

對不起,我的蟒蛇有點生疏,所以如果我錯過了某些東西,我不會感到驚訝。