Python socket.settimeout() 在连接到已关闭的域时无法正常工作

Python socket.settimeout() not working when connecting to a domain that is down

我需要快速检查主机是启动还是关闭。为此,我在建立连接之前添加了一个套接字超时,但套接字超时似乎不起作用。为什么?

这是我的代码:

import socket

def test_hostname(hostname, port):
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1.0) # timeout
    try:
        s.connect((hostname, port))
    except (socket.timeout, socket.gaierror):
        return False
    else:
        return True
    finally:
        s.close()

result = test_hostname("001.com",80) # 001.com is down

print(result)

我需要大约 10-20 秒而不是指定的超时(1 秒)。

发生这种情况是因为 Python 套接字的超时 而不是 限制了花在 DNS 查找上的时间。由于您正在使用需要解析为 IP 的域名,因此您的 s.connect((hostname, port)) 将首先需要对 hostname 进行 DNS 查询,然后才能使用一个创建连接由 DNS 服务器(如果有)编辑的 IP return。

001.com 似乎无法解析任何 IP 地址,因此您的系统可能需要几秒钟才能 return 失败。发生这种情况时,Python 将退出并且连接将失败,但是超时已经过期很久了。

如果您知道要访问的主机的 IP 地址,请使用该地址代替主机名。如果没有,您将不得不使用 DNS 解析库来超时 DNS 解析,例如 dnspython:

import socket
import dns.resolver

def test_hostname(hostname, port):
    resolver = dns.resolver.Resolver()
    resolver.timeout = resolver.lifetime = 1

    try:
        answer = resolver.resolve(hostname, 'A')
    except dns.exception.Timeout:
        return False

    if not answer.rrset:
        return False

    addr = next(iter(answer.rrset)).to_text()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)

    try:
        s.connect((addr, port))
    except (socket.timeout, socket.gaierror):
        return False
    else:
        return True
    finally:
        s.close()