Обробка Python socket.error: [Errno 104] Підключення скинуто рівноправним


79

При використанні Python 2.7 urllib2для отримання даних з API я отримую помилку [Errno 104] Connection reset by peer. Що спричиняє помилку та як слід обробляти помилку, щоб сценарій не вийшов з ладу?

ticker.py

def urlopen(url):
    response = None
    request = urllib2.Request(url=url)
    try:
        response = urllib2.urlopen(request).read()
    except urllib2.HTTPError as err:
        print "HTTPError: {} ({})".format(url, err.code)
    except urllib2.URLError as err:
        print "URLError: {} ({})".format(url, err.reason)
    except httplib.BadStatusLine as err:
        print "BadStatusLine: {}".format(url)
    return response

def get_rate(from_currency="EUR", to_currency="USD"):
    url = "https://finance.yahoo.com/d/quotes.csv?f=sl1&s=%s%s=X" % (
        from_currency, to_currency)
    data = urlopen(url)
    if "%s%s" % (from_currency, to_currency) in data:
        return float(data.strip().split(",")[1])
    return None


counter = 0
while True:

    counter = counter + 1
    if counter==0 or counter%10:
        rateEurUsd = float(get_rate('EUR', 'USD'))

    # does more stuff here

Простежити

Traceback (most recent call last):
  File "/var/www/testApp/python/ticker.py", line 71, in <module>
    rateEurUsd = float(get_rate('EUR', 'USD'))
  File "/var/www/testApp/python/ticker.py", line 29, in get_exchange_rate
    data = urlopen(url)
  File "/var/www/testApp/python/ticker.py", line 16, in urlopen
    response = urllib2.urlopen(request).read()
  File "/usr/lib/python2.7/urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 438, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 625, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 438, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 625, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python2.7/urllib2.py", line 400, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 418, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1207, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1180, in do_open
    r = h.getresponse(buffering=True)
  File "/usr/lib/python2.7/httplib.py", line 1030, in getresponse
    response.begin()
  File "/usr/lib/python2.7/httplib.py", line 407, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python2.7/httplib.py", line 365, in _read_status
    line = self.fp.readline()
  File "/usr/lib/python2.7/socket.py", line 447, in readline
    data = self._sock.recv(self._rbufsize)
socket.error: [Errno 104] Connection reset by peer
error: Forever detected script exited with code: 1

У Arch Linux, get_rate для мене чудово працює. Ви впевнені, що вас не фільтрують? Чи можете ви завантажити цю URL-адресу в браузер?
korylprince

@korylprince Це чудово працює у браузері, і сценарій працює нормально деякий час, перш ніж помилка почне з'являтися. Якщо я не можу уникнути помилки, як слід обробляти помилку, щоб вона не аварійно працювала, і, ймовірно, використовувала останнє отримане значення?
Athena Wisdom

Відповіді:


133

"Скидання з’єднання за допомогою однорангової мережі" - це еквівалент TCP / IP, якщо натиснути телефон назад на гачок. Це більш чемно, ніж просто не відповісти, залишивши одного повішеним. Але це не FIN-ACK, який очікується від справді ввічливого розмовника TCP / IP. ( З іншої відповіді SO )

Тож ви нічого з цим не зробите, це проблема сервера.

Але ви можете використовувати try .. exceptблок для обробки цього винятку:

from socket import error as SocketError
import errno

try:
    response = urllib2.urlopen(request).read()
except SocketError as e:
    if e.errno != errno.ECONNRESET:
        raise # Not error we are looking for
    pass # Handle error here.

4
Чи правда, що адміністратор сервера зазвичай використовує цей метод, щоб блокувати потенційні запити на вишкрібання клієнтом, чи це, швидше за все, ненавмисна помилка? Тепер мені цікаво, заблокований я навмисно чи ні ...
Blaszard

У деяких випадках це може статися через помилку в іншій частині системи. У моєму випадку на моїй стороні сервера було багато CLOSE_WAIT tcp-з'єднань, і кількість була більшою, ніж могла б забезпечити серверна програма (java дозволяє максимум 50 з'єднань одночасно). Отже, моя серверна програма просто заперечувала нові спроби підключення, скидаючи підключення приблизно через 50 підключень CLOSE_WAIT.
u.unver34

не є носієм англійської мови. Що означає "ляпати телефон назад на гачок". маю на увазі?
Буратіно,

1
@Pinocchio Я також не є носієм мови, але це також існує на моїй мові і стосується застарілої телефонної технології: en.wikipedia.org/wiki/Telephone_hook У старих телефонах, коли ти береш телефон, гачок автоматично вмикається з'єднання. Коли ви знову кладете телефон на гачок - він від'єднується. Отже, в основному сучасною мовою це означає "натиснути кнопку відключення / червону, залишити дзвінок".
Буник

13

Ви можете спробувати додати кілька time.sleepдзвінків до свого коду.

Здається, сторона сервера обмежує кількість запитів на одиницю часу (годину, день, секунду) як проблему безпеки. Вам потрібно вгадати, скільки (можливо, використовуючи інший сценарій з лічильником?), І налаштувати свій сценарій, щоб не перевищити цю межу.

Щоб уникнути збою вашого коду, спробуйте вловити цю помилку try .. exceptнавколо викликів urllib2.


1

Існує спосіб вловити помилку безпосередньо в реченні exclu з ConnectionResetError, краще виділити правильну помилку. Цей приклад також фіксує час очікування.

from urllib.request import urlopen 
from socket import timeout

url = "http://......"
try: 
    string = urlopen(url, timeout=5).read()
except ConnectionResetError:
    print("==> ConnectionResetError")
    pass
except timeout: 
    print("==> Timeout")
    pass
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.