Закрыть соединение urllib2 - PullRequest
9 голосов
/ 26 марта 2011

Я использую urllib2 для загрузки файлов с ftp- и http-серверов.

Некоторые из серверов поддерживают только одно соединение на IP. Проблема в том, что urllib2 не закрывает соединение мгновенно. Посмотрите на пример программы.

from urllib2 import urlopen
from time import sleep

url = 'ftp://user:pass@host/big_file.ext'

def load_file(url):
    f = urlopen(url)
    loaded = 0
    while True:
        data = f.read(1024)
        if data == '':
            break
        loaded += len(data)
    f.close()
    #sleep(1)
    print('loaded {0}'.format(loaded))

load_file(url)
load_file(url)

Код загружает два файла (здесь два файла одинаковы) с ftp-сервера, который поддерживает только 1 соединение. Это напечатает следующий журнал:

loaded 463675266
Traceback (most recent call last):
  File "conection_test.py", line 20, in <module>
    load_file(url)
  File "conection_test.py", line 7, in load_file
    f = urlopen(url)
  File "/usr/lib/python2.6/urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.6/urllib2.py", line 391, in open
    response = self._open(req, data)
  File "/usr/lib/python2.6/urllib2.py", line 409, in _open
    '_open', req)
  File "/usr/lib/python2.6/urllib2.py", line 369, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.6/urllib2.py", line 1331, in ftp_open
    fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout)
  File "/usr/lib/python2.6/urllib2.py", line 1352, in connect_ftp
    fw = ftpwrapper(user, passwd, host, port, dirs, timeout)
  File "/usr/lib/python2.6/urllib.py", line 854, in __init__
    self.init()
  File "/usr/lib/python2.6/urllib.py", line 860, in init
    self.ftp.connect(self.host, self.port, self.timeout)
  File "/usr/lib/python2.6/ftplib.py", line 134, in connect
    self.welcome = self.getresp()
  File "/usr/lib/python2.6/ftplib.py", line 216, in getresp
    raise error_temp, resp
urllib2.URLError: <urlopen error ftp error: 421 There are too many connections from your internet address.>

Таким образом, первый файл загружается, а второй выходит из строя, поскольку первое соединение не было закрыто.

Но когда я использую sleep(1) после f.close(), ошибка не возникает:

loaded 463675266
loaded 463675266

Есть ли способ принудительно закрыть соединение, чтобы вторая загрузка не завершилась?

Ответы [ 4 ]

4 голосов
/ 17 апреля 2013

Причиной действительно является утечка дескриптора файла.Мы также обнаружили, что с jython проблема гораздо более очевидна, чем с cpython.Коллега предложил это решение:


    fdurl = urllib2.urlopen(req,timeout=self.timeout)
    realsock = fdurl.fp._sock.fp._sock** # we want to close the "real" socket later 
    req = urllib2.Request(url, header)
    try:
             fdurl = urllib2.urlopen(req,timeout=self.timeout)
    except urllib2.URLError,e:
              print "urlopen exception", e
    realsock.close() 
    fdurl.close()

Исправление уродливо, но делает работу, больше не "слишком много открытых соединений".

3 голосов
/ 16 апреля 2011

Biggie: Я думаю, это потому, что соединение не закрыто ().

Примечание close () освобождает ресурс, связанный с соединением, но не обязательно немедленно закрывает соединение.Если вы хотите своевременно закрыть соединение, вызовите shutdown () перед close ().

Вы можете попробовать что-то подобное до f.close ():

import socket
f.fp._sock.fp._sock.shutdown(socket.SHUT_RDWR)

(И да .. если это работает, это не правильно (тм), но вы поймете, в чем проблема.)

3 голосов
/ 17 сентября 2011

что касается Python 2.7.1, urllib2 действительно пропускает дескриптор файла: https://bugs.pypy.org/issue867

0 голосов
/ 26 марта 2011

Алекс Мартелли отвечает на аналогичный вопрос.Прочитайте это: я должен вызвать close () после urllib.urlopen ()?

В двух словах:

import contextlib

with contextlib.closing(urllib.urlopen(u)) as x:
    # ...
...