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

Posted

技术标签:

【中文标题】Python 处理 socket.error:[Errno 104] 连接由对等方重置【英文标题】:Python handling socket.error: [Errno 104] Connection reset by peer 【发布时间】:2014-01-01 07:45:09 【问题描述】:

当使用 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 它在浏览器中运行良好,并且脚本在错误开始出现之前运行了一段时间。如果我无法避免错误,应该如何处理错误以使其不会崩溃,并且可能使用检索到的最新值? 【参考方案1】:

“对等方重置连接”是 TCP/IP 等价物,相当于将电话重新挂上。这比仅仅不回复,留下一个悬而未决更有礼貌。但这并不是真正礼貌的 TCP/IP 话务员所期望的 FIN-ACK。 (From other SO answer)

所以你无能为力,这是服务器的问题。

但您可以使用 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.

【讨论】:

服务器的管理员是否经常使用这种方法来阻止客户端潜在的抓取请求,或者它更可能只是一个无意的错误?现在我想知道我是否被故意屏蔽了...... 在某些情况下,它可能是由于系统另一部分的错误而发生的。就我而言,我的服务器端有许多 CLOSE_WAIT tcp 连接,并且数量大于服务器应用程序可以提供的数量(java 一次最多允许 50 个连接)。所以我的服务器端应用程序只是通过在大约 50 个 CLOSE_WAIT 挂起连接后重置连接来拒绝新的连接尝试。 不是以英语为母语的人。什么是“砰地一声把电话挂在钩子上”。什么意思? @Pinocchio 我也不是母语,但这也存在于我的语言中,指的是过时的电话技术:en.wikipedia.org/wiki/Telephone_hook 旧电话,当你拿起电话时,挂钩会自动转动在连接上。当您将电话放回挂钩上时-它已断开连接。所以基本上在现代语言中,它的意思是“按下断开/红色按钮,离开通话”。 所以如果我可以控制服务器和客户端——我可以做些什么来在服务器端解决这个问题?谢谢!【参考方案2】:

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

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

为了避免您的代码崩溃,请尝试在 urllib2 调用周围使用 try .. except 捕获此错误。

【讨论】:

【参考方案3】:

有一种方法可以直接在except子句中用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

【讨论】:

以上是关于Python 处理 socket.error:[Errno 104] 连接由对等方重置的主要内容,如果未能解决你的问题,请参考以下文章

python socket.error: [Errno 10061]

[译]Python - socket.error: Cannot assign requested address

python socket.error: [Errno 10054] 解决方法

python绑定socket.error:[Errno 13]权限被拒绝

python发邮脚本运行每次都报socket.error: [Errno 110] Connection timed out ,用别人的机器也报

Python rpyc“socket.error:[Errno 113]没有到主机的路由”