python asyncio.open_connection()超时时行为不一致

python asyncio.open_connection()超时时行为不一致,python,asynchronous,tcp,timeout,python-asyncio,Python,Asynchronous,Tcp,Timeout,Python Asyncio,我正在使用Python3.7和asyncio来创建一个到本地网络上某些硬件的TCP连接,这些硬件和所有硬件一样,可能会断开连接,或者干脆关闭 如果硬件断开连接,我注意到使用asyncio.open\u connection()大约需要~3秒钟才能引发OSError: import asyncio import logging logging.basicConfig(level='INFO', format='%(asctime)s %(message)s') async def main(ho

我正在使用Python3.7和asyncio来创建一个到本地网络上某些硬件的TCP连接,这些硬件和所有硬件一样,可能会断开连接,或者干脆关闭

如果硬件断开连接,我注意到使用
asyncio.open\u connection()
大约需要~3秒钟才能引发
OSError

import asyncio
import logging
logging.basicConfig(level='INFO', format='%(asctime)s %(message)s')

async def main(host, port):
    while True:
    try:
        logging.info('trying to connect to %s:%d', host, port)
        r, w = await asyncio.open_connection(host, port)
        logging.info('connected')
        break
    except Exception as error:
        logging.error('error: %r', error)

asyncio.run(main('cryoctrl01', 5000))
输出:

$ python test.py
2020-05-05 18:31:04,983 trying to connect to cryoctrl01:5000
2020-05-05 18:31:08,039 error: OSError(113, "Connect call failed ('192.168.1.12', 5000)")
2020-05-05 18:31:08,039 trying to connect to cryoctrl01:5000
2020-05-05 18:31:11,111 error: OSError(113, "Connect call failed ('192.168.1.12', 5000)")
2020-05-05 18:31:11,111 trying to connect to cryoctrl01:5000
2020-05-05 18:31:14,183 error: OSError(113, "Connect call failed ('192.168.1.12', 5000)")
...
正如我所知,我在本地网络上,我想减少这一时间,所以我将代码包装为一个
asyncio.wait_for()
,如下所示:

import asyncio
import logging
logging.basicConfig(level='INFO', format='%(asctime)s %(message)s')

async def main(host, port, timeout):
  while True:
      try:
          logging.info('trying to connect to %s:%d', host, port)
          coro = asyncio.open_connection(host, port)
          r, w = await asyncio.wait_for(coro, timeout)
          logging.info('connected')
          break
      except Exception as error:
          logging.error('error: %r', error)

asyncio.run(main('cryoctrl01', 5000, 0.5))
但现在我有一个不一致的行为:有时我会得到预期的
超时
错误,但有时我会得到一个
操作错误

$ python test.py
2020-05-05 18:56:18,035 trying to connect to cryoctrl01:5000
2020-05-05 18:56:18,537 error: TimeoutError()
2020-05-05 18:56:18,537 trying to connect to cryoctrl01:5000
2020-05-05 18:56:19,038 error: TimeoutError()
2020-05-05 18:56:19,038 trying to connect to cryoctrl01:5000
2020-05-05 18:56:19,540 error: TimeoutError()
2020-05-05 18:56:19,540 trying to connect to cryoctrl01:5000
2020-05-05 18:56:20,042 error: TimeoutError()
2020-05-05 18:56:20,043 trying to connect to cryoctrl01:5000
2020-05-05 18:56:20,545 error: TimeoutError()
2020-05-05 18:56:20,545 trying to connect to cryoctrl01:5000
2020-05-05 18:56:21,047 error: TimeoutError()
2020-05-05 18:56:21,047 trying to connect to cryoctrl01:5000
2020-05-05 18:56:21,095 error: OSError(113, "Connect call failed ('192.168.1.12', 5000)")
2020-05-05 18:56:21,095 trying to connect to cryoctrl01:5000
2020-05-05 18:56:21,597 error: TimeoutError()
2020-05-05 18:56:21,597 trying to connect to cryoctrl01:5000
2020-05-05 18:56:22,098 error: TimeoutError()
2020-05-05 18:56:22,098 trying to connect to cryoctrl01:5000
2020-05-05 18:56:22,600 error: TimeoutError()
2020-05-05 18:56:22,600 trying to connect to cryoctrl01:5000
2020-05-05 18:56:23,102 error: TimeoutError()
2020-05-05 18:56:23,102 trying to connect to cryoctrl01:5000
2020-05-05 18:56:23,604 error: TimeoutError()
2020-05-05 18:56:23,604 trying to connect to cryoctrl01:5000
2020-05-05 18:56:24,106 error: TimeoutError()
2020-05-05 18:56:24,106 trying to connect to cryoctrl01:5000
2020-05-05 18:56:24,167 error: OSError(113, "Connect call failed ('192.168.1.12', 5000)")
2020-05-05 18:56:24,167 trying to connect to cryoctrl01:5000
2020-05-05 18:56:24,669 error: TimeoutError()
2020-05-05 18:56:24,669 trying to connect to cryoctrl01:5000
我注意到这种情况似乎每3秒就会发生一次。 这种行为是来自操作系统吗?
我应该如何更改代码以获得一致的行为?

只有在实际超时时才能获得
TimeoutErrr
。如果查看启动连接和非超时错误之间的时间差,您将看到它们发生在小于0.5s的超时时间内。检查超时并不能保护您免受其他网络错误的影响,所以您不能期望一致性—您只需要处理这两种情况

如果使用
%s
而不是
%r
打印错误,您将看到来自系统的错误消息。Errno 113可能是“没有到主机的路由”,这可能表示您的网络出现暂时性故障。您需要捕获
OSError
并决定如何处理它们,典型的方法是在适当的延迟后重试连接