Python处理socket.error:[Errno 104]对等重置连接


78

当使用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对我来说工作正常。您确定没有被过滤吗?您可以在浏览器中加载该网址吗?
korylprince 2013年

@korylprince在浏览器中可以正常工作,并且脚本可以在出现错误之前运行一段时间。如果我无法避免该错误,应该如何处理该错误,以使其不会崩溃,并可能使用检索到的最新值?
雅典娜智慧

Answers:


132

“由对等方重置连接”是TCP / IP的等效功能,它可以将电话重新挂断。礼貌比不回信,挂个电话更礼貌。但这不是真正礼貌的TCP / IP对话者所期望的FIN-ACK。(从其他答案

因此,您无能为力,这是服务器的问题。

但是您可以使用try .. exceptblock处理该异常:

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 对于旧电话,当您拿起电话时,挂钩会自动打开连接。当您将手机重新挂上时-断开了连接。因此,基本上用现代语言来说,它的意思是“按断开/红色按钮,离开电话”。
Bunyk

13

您可以尝试将一些time.sleep调用添加到您的代码中。

似乎服务器端限制了每个时间单位(小时,天,秒)的请求量,这是一个安全问题。您需要猜测有多少(也许要使用另一个带有计数器的脚本?)并调整您的脚本以使其不超过此限制。

为了避免您的代码崩溃,请尝试try .. except通过urllib2调用来捕获此错误。


1

有一种方法可以使用ConnectionResetError直接在except子句中捕获错误,更好地隔离正确的错误。此示例还捕获了超时。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.